@sentry/junior-dashboard 0.263.0 → 0.265.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 (197) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +2 -5
  3. package/dist/app.js +119 -35
  4. package/dist/client.js +148 -135
  5. package/dist/index.d.ts +0 -1
  6. package/dist/index.js +119 -35
  7. package/dist/tailwind.css +1 -1
  8. package/package.json +9 -4
  9. package/src/api/schema.ts +0 -1
  10. package/src/app.ts +5 -20
  11. package/src/client/App.tsx +29 -31
  12. package/src/client/api.ts +7 -0
  13. package/src/client/components/BottomSheet.tsx +26 -18
  14. package/src/client/components/CopyButton.tsx +83 -0
  15. package/src/client/components/DetailsPanel.tsx +133 -0
  16. package/src/client/components/Drawer.tsx +34 -33
  17. package/src/client/components/FilterBar.tsx +16 -31
  18. package/src/client/components/Metric.tsx +33 -23
  19. package/src/client/components/PageContentSkeleton.tsx +1 -1
  20. package/src/client/components/Pagination.tsx +1 -1
  21. package/src/client/components/ParticipantAvatarStack.tsx +40 -36
  22. package/src/client/components/ProfileMenu.tsx +27 -93
  23. package/src/client/components/README.md +101 -64
  24. package/src/client/components/SegmentedNav.tsx +1 -1
  25. package/src/client/components/ShimmerText.tsx +2 -6
  26. package/src/client/components/ThemeSwitcher.tsx +19 -15
  27. package/src/client/components/charts/ActivityChart.tsx +173 -288
  28. package/src/client/components/charts/CacheWrites.tsx +63 -50
  29. package/src/client/components/charts/InputCacheChart.tsx +137 -131
  30. package/src/client/components/charts/README.md +4 -4
  31. package/src/client/components/charts/SystemMetricCharts.tsx +68 -95
  32. package/src/client/components/charts/WorkspaceUsageChart.tsx +42 -62
  33. package/src/client/components/controls/DirectorySortSelect.tsx +3 -3
  34. package/src/client/components/controls/TimeRangeSelector.tsx +10 -15
  35. package/src/client/components/layout/DashboardHeader.tsx +44 -90
  36. package/src/client/components/layout/SecondaryNavigation.tsx +2 -1
  37. package/src/client/components/layout/VisualViewportShell.tsx +2 -2
  38. package/src/client/components/ui/accordion.tsx +77 -0
  39. package/src/client/components/ui/alert-dialog.tsx +89 -0
  40. package/src/client/components/ui/button.tsx +85 -0
  41. package/src/client/components/ui/chart.tsx +74 -0
  42. package/src/client/components/ui/checkbox.tsx +26 -0
  43. package/src/client/components/ui/collapsible.tsx +22 -0
  44. package/src/client/components/ui/field.tsx +49 -0
  45. package/src/client/components/ui/input.tsx +35 -0
  46. package/src/client/components/ui/native-select.tsx +57 -0
  47. package/src/client/components/ui/popover.tsx +67 -0
  48. package/src/client/components/ui/radio-group.tsx +32 -0
  49. package/src/client/components/ui/sheet.tsx +151 -0
  50. package/src/client/components/ui/skeleton.tsx +19 -0
  51. package/src/client/components/ui/table.tsx +80 -0
  52. package/src/client/components/ui/tabs.tsx +75 -0
  53. package/src/client/components/ui/textarea.tsx +17 -0
  54. package/src/client/components/ui/toast.tsx +215 -0
  55. package/src/client/components/ui/toggle-group.tsx +36 -0
  56. package/src/client/components/ui/toggle.tsx +37 -0
  57. package/src/client/components/ui/tooltip.tsx +168 -0
  58. package/src/client/components/ui/trace.tsx +96 -0
  59. package/src/client/components/ui/useSheet.ts +74 -0
  60. package/src/client/conversations/ChatLayout.tsx +7 -3
  61. package/src/client/conversations/ConversationBrief.tsx +17 -6
  62. package/src/client/conversations/ConversationChildAgents.tsx +87 -58
  63. package/src/client/conversations/ConversationComposer.tsx +26 -12
  64. package/src/client/conversations/ConversationDetails.tsx +198 -95
  65. package/src/client/conversations/ConversationEventDetails.tsx +141 -0
  66. package/src/client/conversations/ConversationEventLog.tsx +150 -129
  67. package/src/client/conversations/ConversationHeader.tsx +8 -3
  68. package/src/client/conversations/ConversationHeaderActions.tsx +90 -82
  69. package/src/client/conversations/ConversationHomeList.tsx +1 -1
  70. package/src/client/conversations/ConversationLinkedWork.tsx +118 -0
  71. package/src/client/conversations/ConversationMeta.tsx +111 -92
  72. package/src/client/conversations/ConversationPage.tsx +241 -110
  73. package/src/client/conversations/ConversationSidebar.tsx +48 -29
  74. package/src/client/conversations/ConversationUsageBreakdown.tsx +16 -3
  75. package/src/client/conversations/ConversationWatches.tsx +165 -0
  76. package/src/client/conversations/ConversationWorkspace.tsx +12 -22
  77. package/src/client/conversations/ConversationWorkspaceLoading.tsx +1 -1
  78. package/src/client/conversations/DetailsList.tsx +124 -0
  79. package/src/client/conversations/EventDetails.tsx +42 -8
  80. package/src/client/conversations/ForkConversationButton.tsx +27 -16
  81. package/src/client/conversations/ObjectCard.tsx +20 -8
  82. package/src/client/conversations/PendingMailboxStack.tsx +52 -33
  83. package/src/client/conversations/README.md +93 -19
  84. package/src/client/conversations/ShareConversationButton.tsx +28 -3
  85. package/src/client/conversations/SubagentTranscriptDrawer.tsx +21 -15
  86. package/src/client/conversations/ToolFrame.tsx +36 -52
  87. package/src/client/conversations/TranscriptActivityGroup.tsx +13 -4
  88. package/src/client/conversations/TranscriptAutomationTriggerView.tsx +5 -4
  89. package/src/client/conversations/TranscriptContextEventView.tsx +25 -11
  90. package/src/client/conversations/TranscriptHeadingRow.tsx +7 -7
  91. package/src/client/conversations/TranscriptLoading.tsx +1 -1
  92. package/src/client/conversations/TranscriptMarkdown.tsx +25 -20
  93. package/src/client/conversations/TranscriptMessageContextView.tsx +8 -2
  94. package/src/client/conversations/TranscriptMessageView.tsx +6 -1
  95. package/src/client/conversations/TranscriptReasoningView.tsx +54 -68
  96. package/src/client/conversations/TranscriptSubagentView.tsx +3 -3
  97. package/src/client/conversations/TranscriptTimestamp.tsx +9 -7
  98. package/src/client/conversations/TranscriptToolView.tsx +1 -1
  99. package/src/client/conversations/TranscriptTurnContextView.tsx +99 -27
  100. package/src/client/conversations/TranscriptView.tsx +63 -27
  101. package/src/client/conversations/eventLog.ts +57 -0
  102. package/src/client/conversations/eventTime.ts +60 -0
  103. package/src/client/conversations/queries.ts +29 -0
  104. package/src/client/conversations/transcriptBottomPinning.ts +34 -0
  105. package/src/client/format.ts +5 -0
  106. package/src/client/lib/utils.ts +1 -0
  107. package/src/client/pages/ConnectionsPage.tsx +2 -2
  108. package/src/client/pages/PersonalTokensPage.tsx +4 -4
  109. package/src/client/pages/SettingsPage.tsx +9 -10
  110. package/src/client/pages/admin/ServiceAccountsSection.tsx +2 -2
  111. package/src/client/pages/automations/AutomationCostChart.tsx +50 -71
  112. package/src/client/pages/automations/AutomationDeleteDialog.tsx +87 -0
  113. package/src/client/pages/automations/AutomationDetails.tsx +193 -0
  114. package/src/client/pages/automations/AutomationEditPage.tsx +7 -8
  115. package/src/client/pages/automations/AutomationEditor.tsx +41 -29
  116. package/src/client/pages/automations/AutomationEditorLoading.tsx +1 -1
  117. package/src/client/pages/automations/AutomationEventFields.tsx +202 -119
  118. package/src/client/pages/automations/AutomationExecutionChart.tsx +51 -64
  119. package/src/client/pages/automations/AutomationExecutionStatusChart.tsx +47 -70
  120. package/src/client/pages/automations/AutomationExecutionsPage.tsx +150 -126
  121. package/src/client/pages/automations/AutomationFilters.tsx +189 -74
  122. package/src/client/pages/automations/AutomationLifecycle.tsx +109 -78
  123. package/src/client/pages/automations/AutomationOutcomeFields.tsx +26 -24
  124. package/src/client/pages/automations/AutomationRecentRuns.tsx +143 -0
  125. package/src/client/pages/automations/AutomationRow.tsx +12 -138
  126. package/src/client/pages/automations/AutomationRunDetails.tsx +307 -0
  127. package/src/client/pages/automations/AutomationRunsPage.tsx +119 -94
  128. package/src/client/pages/automations/AutomationScheduleFields.tsx +51 -51
  129. package/src/client/pages/automations/AutomationStatus.tsx +201 -0
  130. package/src/client/pages/automations/AutomationVersionsPage.tsx +65 -28
  131. package/src/client/pages/automations/AutomationsPage.tsx +80 -68
  132. package/src/client/pages/automations/AutomationsPageLayout.tsx +46 -14
  133. package/src/client/pages/automations/README.md +57 -0
  134. package/src/client/pages/automations/TriggerFilters.tsx +22 -0
  135. package/src/client/pages/automations/automationFormat.ts +37 -23
  136. package/src/client/pages/code/CodeActivityChart.tsx +59 -101
  137. package/src/client/pages/code/CodePage.tsx +52 -55
  138. package/src/client/pages/code/RepositoryPage.tsx +6 -8
  139. package/src/client/pages/locations/LocationActivityChart.tsx +53 -70
  140. package/src/client/pages/locations/LocationDirectoryActivityChart.tsx +60 -80
  141. package/src/client/pages/memory/MemoryCostChart.tsx +79 -103
  142. package/src/client/pages/memory/MemoryDetails.tsx +218 -0
  143. package/src/client/pages/memory/MemoryPage.tsx +168 -112
  144. package/src/client/pages/memory/MemoryPageLayout.tsx +33 -9
  145. package/src/client/pages/memory/MemoryTimeline.tsx +46 -69
  146. package/src/client/pages/memory/memoryRecord.ts +1 -1
  147. package/src/client/pages/people/ContributionGrid.tsx +23 -16
  148. package/src/client/pages/people/PeopleActivityChart.tsx +68 -107
  149. package/src/client/pages/people/ProfileCodeChanges.tsx +5 -5
  150. package/src/client/pages/system/BaseSandboxCard.tsx +1 -1
  151. package/src/client/pages/system/ConversationActivityChart.tsx +49 -67
  152. package/src/client/pages/system/GuardianActivity.tsx +1 -2
  153. package/src/client/pages/system/PluginBarChart.tsx +106 -87
  154. package/src/client/pages/system/PluginReportRecordSet.tsx +25 -24
  155. package/src/client/pages/system/README.md +23 -0
  156. package/src/client/pages/system/SystemNavigation.tsx +5 -1
  157. package/src/client/pages/system/WorkspaceDeleteDialog.tsx +111 -0
  158. package/src/client/pages/system/WorkspaceDetails.tsx +200 -43
  159. package/src/client/pages/system/WorkspaceEditor.tsx +59 -61
  160. package/src/client/pages/system/WorkspaceFormPage.tsx +23 -47
  161. package/src/client/pages/system/WorkspaceList.tsx +20 -30
  162. package/src/client/pages/system/WorkspacesPage.tsx +117 -119
  163. package/src/client/pages/system/sandboxSize.ts +8 -0
  164. package/src/client/pages/system/workspaceQueries.ts +53 -0
  165. package/src/client/pages/user/PluginUserPage.tsx +51 -2
  166. package/src/client/pages/user/pluginUserPageData.ts +1 -6
  167. package/src/client/styles.ts +4 -0
  168. package/src/client.tsx +7 -1
  169. package/src/mock-reporting/automation-collection.ts +4 -2
  170. package/src/mock-reporting/fixtures.ts +23 -7
  171. package/src/mock-reporting/routes.ts +12 -0
  172. package/src/mock-reporting/watches.ts +94 -0
  173. package/src/shell.ts +4 -9
  174. package/src/tailwind.css +77 -10
  175. package/src/client/components/Button.tsx +0 -118
  176. package/src/client/components/Field.tsx +0 -53
  177. package/src/client/components/SegmentedTabs.tsx +0 -119
  178. package/src/client/components/Select.tsx +0 -52
  179. package/src/client/components/Sheet.LICENSE +0 -21
  180. package/src/client/components/Sheet.tsx +0 -141
  181. package/src/client/components/Skeleton.tsx +0 -17
  182. package/src/client/components/TextInput.tsx +0 -58
  183. package/src/client/components/Tooltip.tsx +0 -216
  184. package/src/client/components/charts/useChartLayout.ts +0 -27
  185. package/src/client/conversations/ConversationDetailsFrame.tsx +0 -91
  186. package/src/client/conversations/CopyMarkdownButton.tsx +0 -65
  187. package/src/client/pages/automations/AutomationDetailsDrawer.tsx +0 -219
  188. package/src/client/pages/dev/ChartFixtures.tsx +0 -325
  189. package/src/client/pages/dev/ComponentsPage.tsx +0 -899
  190. package/src/client/pages/dev/ConversationFixture.tsx +0 -231
  191. package/src/client/pages/dev/ObjectFixtures.tsx +0 -138
  192. package/src/client/pages/dev/SegmentedFixtures.tsx +0 -59
  193. package/src/client/pages/dev/SelectFixtures.tsx +0 -62
  194. package/src/client/pages/dev/TranscriptActivityFixtures.tsx +0 -206
  195. package/src/client/pages/memory/MemoryDetailsDrawer.tsx +0 -167
  196. package/src/client/pages/system/SandboxSizeSummary.tsx +0 -47
  197. package/src/client/pages/system/SnapshotSummary.tsx +0 -78
@@ -0,0 +1,193 @@
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
+ }
@@ -8,10 +8,11 @@ import {
8
8
  import { useAutomationData } from "../../api";
9
9
  import { useDocumentTitle } from "../../documentTitle";
10
10
  import { fetchDashboardJson } from "../../http";
11
- import { Button } from "../../components/Button";
11
+ import { Button } from "../../components/ui/button";
12
12
  import { FormNotice } from "../../components/FormNotice";
13
13
  import { StatusChip } from "../../components/StatusChip";
14
14
  import { AutomationsNavigation } from "./AutomationsPageLayout";
15
+ import { TriggerFilters } from "./TriggerFilters";
15
16
  import { AutomationEditor } from "./AutomationEditor";
16
17
  import { AutomationEditorLoading } from "./AutomationEditorLoading";
17
18
  import { AutomationFormSection } from "./AutomationFormSection";
@@ -49,7 +50,7 @@ export function AutomationEditPage(props: { enabled: boolean }) {
49
50
  value?.kind === "event"
50
51
  ? value.status !== "active"
51
52
  ? value.status
52
- : value.triggerAvailable
53
+ : value.trigger.problems.length === 0
53
54
  ? "active"
54
55
  : "unavailable"
55
56
  : value?.status;
@@ -144,12 +145,10 @@ function AutomationReadOnly({ automation }: { automation: AutomationSummary }) {
144
145
  ) : (
145
146
  <div className="grid gap-3 text-sm">
146
147
  <p className="m-0 break-words">{automation.resource}</p>
147
- <p className="m-0 break-words">{automation.events.join(", ")}</p>
148
- {automation.match ? (
149
- <pre className="whitespace-pre-wrap break-words text-xs">
150
- {JSON.stringify(automation.match, null, 2)}
151
- </pre>
152
- ) : null}
148
+ <p className="m-0 break-words">
149
+ {automation.trigger.events.join(", ")}
150
+ </p>
151
+ <TriggerFilters filters={automation.trigger.filters} />
153
152
  </div>
154
153
  )}
155
154
  </AutomationFormSection>
@@ -8,10 +8,13 @@ import {
8
8
  type AutomationEdit,
9
9
  type AutomationSummary,
10
10
  } from "@sentry/junior/api/schema";
11
- import { Field } from "../../components/Field";
12
- import { TextArea, TextInput } from "../../components/TextInput";
13
- import { Button } from "../../components/Button";
11
+ import { Button } from "../../components/ui/button";
12
+ import { Input } from "../../components/ui/input";
13
+ import { Textarea } from "../../components/ui/textarea";
14
+ import { Field, FieldError, FieldLabel } from "../../components/ui/field";
15
+ import { useToastManager } from "../../components/ui/toast";
14
16
  import { FormNotice } from "../../components/FormNotice";
17
+ import { RadioGroup, RadioGroupItem } from "../../components/ui/radio-group";
15
18
  import { DashboardApiError, fetchDashboardJson, patch } from "../../http";
16
19
  import { AutomationFormSection } from "./AutomationFormSection";
17
20
  import { AutomationScheduleFields } from "./AutomationScheduleFields";
@@ -33,6 +36,7 @@ export function AutomationEditor(props: {
33
36
  summary: AutomationSummary;
34
37
  returnPath: string;
35
38
  }) {
39
+ const toast = useToastManager();
36
40
  const [original, setOriginal] = useState(props.automation);
37
41
  const [draft, setDraft] = useState(() =>
38
42
  createAutomationDraft(props.automation),
@@ -80,7 +84,12 @@ export function AutomationEditor(props: {
80
84
  await queryClient.invalidateQueries({
81
85
  queryKey: ["dashboard", "automations"],
82
86
  });
83
- navigate(props.returnPath, { state: { automationSaved: true } });
87
+ toast.add({
88
+ title: "Automation saved",
89
+ description: "Changes apply to future work.",
90
+ type: "success",
91
+ });
92
+ navigate(props.returnPath);
84
93
  },
85
94
  onError: (error) => {
86
95
  setFields(error instanceof DashboardApiError ? (error.fields ?? {}) : {});
@@ -246,8 +255,9 @@ export function AutomationEditor(props: {
246
255
  title="What to do"
247
256
  detail="Give Junior a clear instruction and a name you can find later."
248
257
  >
249
- <Field label="Title" htmlFor="title" error={fieldError("title")}>
250
- <TextInput
258
+ <Field>
259
+ <FieldLabel htmlFor="title">Title</FieldLabel>
260
+ <Input
251
261
  size="comfortable"
252
262
  id="title"
253
263
  value={draft.title}
@@ -255,24 +265,24 @@ export function AutomationEditor(props: {
255
265
  onChange={(e) => change({ title: e.target.value })}
256
266
  {...inputState("title")}
257
267
  />
268
+ <FieldError id="title-error">{fieldError("title")}</FieldError>
258
269
  </Field>
259
- <Field
260
- label="Instruction"
261
- htmlFor="instruction"
262
- error={fieldError("instruction")}
263
- >
264
- <TextArea
265
- prose
270
+ <Field>
271
+ <FieldLabel htmlFor="instruction">Instruction</FieldLabel>
272
+ <Textarea
266
273
  id="instruction"
267
274
  rows={8}
268
275
  value={draft.instruction}
269
276
  onChange={(e) => change({ instruction: e.target.value })}
270
277
  {...inputState("instruction")}
271
- className="resize-y text-base sm:text-sm"
278
+ className="resize-y text-base leading-relaxed sm:text-sm"
272
279
  />
273
280
  <p className="m-0 text-right text-xs text-dashboard-text-muted">
274
281
  {draft.instruction.length.toLocaleString()} / 4,000
275
282
  </p>
283
+ <FieldError id="instruction-error">
284
+ {fieldError("instruction")}
285
+ </FieldError>
276
286
  </Field>
277
287
  </AutomationFormSection>
278
288
  <AutomationFormSection
@@ -304,6 +314,7 @@ export function AutomationEditor(props: {
304
314
  <AutomationEventFields
305
315
  value={draft.trigger ?? original.trigger}
306
316
  original={original.trigger}
317
+ disabled={save.isPending}
307
318
  onChange={(trigger) => change({ trigger })}
308
319
  />
309
320
  )}
@@ -323,6 +334,7 @@ export function AutomationEditor(props: {
323
334
  value={draft.outcomes}
324
335
  original={original.outcomes}
325
336
  destination={props.summary.destination}
337
+ disabled={save.isPending}
326
338
  onChange={(outcomes) => change({ outcomes })}
327
339
  />
328
340
  ) : (
@@ -341,8 +353,15 @@ export function AutomationEditor(props: {
341
353
  title="Credentials"
342
354
  detail="Choose which connected accounts Junior can use."
343
355
  >
344
- <fieldset className="divide-y divide-dashboard-border-subtle rounded-md border border-dashboard-border-emphasis">
345
- <legend className="sr-only">Credential mode</legend>
356
+ <RadioGroup
357
+ aria-label="Credential mode"
358
+ disabled={save.isPending}
359
+ className="divide-y divide-dashboard-border-subtle rounded-md border border-dashboard-border-emphasis"
360
+ onValueChange={(credentialMode: "creator" | "system") =>
361
+ change({ credentialMode })
362
+ }
363
+ value={dropsCreatorCredentials ? "system" : draft.credentialMode}
364
+ >
346
365
  {(
347
366
  [
348
367
  [
@@ -365,24 +384,17 @@ export function AutomationEditor(props: {
365
384
  ).map(([value, label, detail]) => (
366
385
  <label
367
386
  key={value}
368
- className="flex cursor-pointer items-start gap-3 px-4 py-4 has-checked:bg-dashboard-fill-soft has-disabled:cursor-not-allowed has-disabled:opacity-60"
387
+ className="flex cursor-pointer items-start gap-3 px-4 py-4 has-data-checked:bg-dashboard-fill-soft has-data-disabled:cursor-not-allowed has-data-disabled:opacity-60"
369
388
  >
370
- <input
371
- type="radio"
372
- name="credentials"
373
- className="mt-0.5 size-4 shrink-0 accent-dashboard-focus"
374
- checked={
375
- dropsCreatorCredentials
376
- ? value === "system"
377
- : draft.credentialMode === value
378
- }
389
+ <RadioGroupItem
390
+ className="mt-0.5"
391
+ value={value}
379
392
  disabled={
380
393
  value === "creator" &&
381
394
  !owned &&
382
395
  (original.credentialMode !== "creator" ||
383
396
  dropsCreatorCredentials)
384
397
  }
385
- onChange={() => change({ credentialMode: value })}
386
398
  />
387
399
  <span>
388
400
  <span className="block text-sm font-medium">{label}</span>
@@ -392,7 +404,7 @@ export function AutomationEditor(props: {
392
404
  </span>
393
405
  </label>
394
406
  ))}
395
- </fieldset>
407
+ </RadioGroup>
396
408
  {dropsCreatorCredentials ? (
397
409
  <FormNotice title="Saving switches this automation to system credentials.">
398
410
  Only {creator} can let a changed instruction
@@ -431,7 +443,7 @@ export function AutomationEditor(props: {
431
443
  </Link>
432
444
  <Button
433
445
  type="submit"
434
- tone="primary"
446
+ variant="primary"
435
447
  className="min-h-11 sm:min-h-9"
436
448
  disabled={!dirty || save.isPending || conflict}
437
449
  >
@@ -1,4 +1,4 @@
1
- import { Skeleton } from "../../components/Skeleton";
1
+ import { Skeleton } from "../../components/ui/skeleton";
2
2
  import { AutomationFormSection } from "./AutomationFormSection";
3
3
 
4
4
  /** Keep the editor's field columns stable while exact values load. */