@sentry/junior-dashboard 0.248.0 → 0.250.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/README.md +3 -0
- package/dist/app.js +2 -2
- package/dist/client.js +40 -38
- package/dist/index.js +2 -2
- package/dist/tailwind.css +1 -1
- package/package.json +3 -3
- package/src/client/App.tsx +19 -5
- package/src/client/components/ActorAvatar.tsx +7 -4
- package/src/client/components/AutomationCard.tsx +2 -2
- package/src/client/components/Button.tsx +12 -11
- package/src/client/components/ConversationList.tsx +3 -3
- package/src/client/components/ConversationSummary.tsx +2 -1
- package/src/client/components/DetailList.tsx +3 -3
- package/src/client/components/Drawer.tsx +2 -2
- package/src/client/components/EmptyTelemetry.tsx +1 -1
- package/src/client/components/Field.tsx +1 -2
- package/src/client/components/FilterBar.tsx +6 -6
- package/src/client/components/Metric.tsx +4 -4
- package/src/client/components/Notice.tsx +7 -7
- package/src/client/components/PageContentSkeleton.tsx +2 -2
- package/src/client/components/Pagination.tsx +2 -2
- package/src/client/components/ParticipantAvatarStack.tsx +4 -4
- package/src/client/components/ProfileMenu.tsx +17 -7
- package/src/client/components/README.md +122 -58
- package/src/client/components/SearchInput.tsx +4 -3
- package/src/client/components/SegmentedNav.tsx +10 -17
- package/src/client/components/SegmentedTabs.tsx +3 -3
- package/src/client/components/Select.tsx +1 -1
- package/src/client/components/SelectableRow.tsx +2 -2
- package/src/client/components/Skeleton.tsx +1 -1
- package/src/client/components/StatusChip.tsx +1 -1
- package/src/client/components/StatusDot.tsx +1 -1
- package/src/client/components/TextInput.tsx +1 -1
- package/src/client/components/Tooltip.tsx +2 -2
- package/src/client/components/VersionDriftBanner.tsx +2 -2
- package/src/client/components/charts/ActivityChart.tsx +5 -6
- package/src/client/components/charts/CacheWrites.tsx +3 -3
- package/src/client/components/charts/ChartHeader.tsx +4 -4
- package/src/client/components/charts/ChartLegend.tsx +1 -1
- package/src/client/components/charts/InputCacheChart.tsx +5 -5
- package/src/client/components/charts/WorkspaceUsageChart.tsx +4 -11
- package/src/client/components/controls/DirectorySortSelect.tsx +2 -2
- package/src/client/components/directory/DirectoryMetric.tsx +2 -2
- package/src/client/components/directory/DirectoryRowsSkeleton.tsx +5 -5
- package/src/client/components/directory/DirectoryTable.tsx +3 -3
- package/src/client/components/layout/CardHeader.tsx +4 -4
- package/src/client/components/layout/DashboardChrome.tsx +9 -3
- package/src/client/components/layout/DashboardHeader.tsx +7 -7
- package/src/client/components/layout/PageHeader.tsx +2 -2
- package/src/client/components/layout/SecondaryNavigation.tsx +7 -14
- package/src/client/components/layout/SectionIntro.tsx +1 -1
- package/src/client/components/layout/SectionTitle.tsx +1 -1
- package/src/client/components/metrics/StatCard.tsx +2 -2
- package/src/client/conversations/ConversationArchiveNotices.tsx +1 -1
- package/src/client/conversations/ConversationBrief.tsx +4 -4
- package/src/client/conversations/ConversationChildAgents.tsx +7 -4
- package/src/client/conversations/ConversationComposer.tsx +8 -7
- package/src/client/conversations/ConversationHeader.tsx +5 -5
- package/src/client/conversations/ConversationHeaderActions.tsx +9 -9
- package/src/client/conversations/ConversationHomeList.tsx +3 -3
- package/src/client/conversations/ConversationListStatusIcon.tsx +1 -1
- package/src/client/conversations/ConversationMemories.tsx +4 -4
- package/src/client/conversations/ConversationMeta.tsx +10 -10
- package/src/client/conversations/ConversationPage.tsx +2 -1
- package/src/client/conversations/ConversationSidebar.tsx +6 -6
- package/src/client/conversations/ConversationUsageBreakdown.tsx +4 -4
- package/src/client/conversations/ConversationWorkspace.tsx +1 -1
- package/src/client/conversations/ConversationWorkspaceLoading.tsx +4 -4
- package/src/client/conversations/CopyMarkdownButton.tsx +1 -1
- package/src/client/conversations/EventUsage.tsx +2 -2
- package/src/client/conversations/PendingMailboxStack.tsx +5 -5
- package/src/client/conversations/README.md +9 -0
- package/src/client/conversations/SubagentTranscriptDrawer.tsx +39 -23
- package/src/client/conversations/ToolFrame.tsx +36 -14
- package/src/client/conversations/TranscriptActivityGroup.tsx +10 -3
- package/src/client/conversations/TranscriptAttachmentsDeliveredView.tsx +4 -6
- package/src/client/conversations/TranscriptContextEventView.tsx +2 -2
- package/src/client/conversations/TranscriptEventView.tsx +1 -1
- package/src/client/conversations/TranscriptMarkdown.tsx +3 -3
- package/src/client/conversations/TranscriptMessageContextView.tsx +7 -7
- package/src/client/conversations/TranscriptMessageView.tsx +2 -6
- package/src/client/conversations/TranscriptRailEvent.tsx +20 -8
- package/src/client/conversations/TranscriptReasoningView.tsx +12 -12
- package/src/client/conversations/TranscriptStructuredEventView.tsx +16 -11
- package/src/client/conversations/TranscriptSubagentView.tsx +64 -39
- package/src/client/conversations/TranscriptText.tsx +5 -4
- package/src/client/conversations/TranscriptToolView.tsx +9 -11
- package/src/client/conversations/TranscriptTurnContextView.tsx +3 -3
- package/src/client/conversations/TranscriptView.tsx +3 -3
- package/src/client/conversations/UsageCard.tsx +1 -1
- package/src/client/conversations/eventTranscript.ts +7 -0
- package/src/client/conversations/transcriptMarkdownLinks.ts +2 -4
- package/src/client/conversations/transcriptStyles.ts +1 -1
- package/src/client/pages/ConnectionsPage.tsx +207 -0
- package/src/client/pages/PersonalTokensPage.tsx +4 -4
- package/src/client/pages/admin/AdminPage.tsx +2 -2
- package/src/client/pages/automations/AutomationCostChart.tsx +2 -2
- package/src/client/pages/automations/AutomationDetailsDrawer.tsx +5 -10
- package/src/client/pages/automations/AutomationEditor.tsx +1 -1
- package/src/client/pages/automations/AutomationExecutionChart.tsx +2 -2
- package/src/client/pages/automations/AutomationExecutionStatusChart.tsx +2 -2
- package/src/client/pages/automations/AutomationExecutionsPage.tsx +4 -4
- package/src/client/pages/automations/AutomationRow.tsx +4 -4
- package/src/client/pages/automations/AutomationRunsPage.tsx +3 -3
- package/src/client/pages/automations/AutomationScheduleFields.tsx +1 -1
- package/src/client/pages/automations/AutomationVersionsPage.tsx +1 -1
- package/src/client/pages/code/CodeActivityChart.tsx +7 -6
- package/src/client/pages/code/CodeChangeRow.tsx +1 -1
- package/src/client/pages/code/CodePage.tsx +5 -5
- package/src/client/pages/code/RepositoryPage.tsx +2 -2
- package/src/client/pages/dev/ChartFixtures.tsx +265 -21
- package/src/client/pages/dev/ComponentsPage.tsx +40 -146
- package/src/client/pages/dev/TranscriptActivityFixtures.tsx +206 -0
- package/src/client/pages/locations/LocationDetailPage.tsx +4 -4
- package/src/client/pages/locations/LocationDirectory.tsx +1 -1
- package/src/client/pages/locations/PrivateActivityCard.tsx +3 -3
- package/src/client/pages/memory/MemoryCostChart.tsx +3 -5
- package/src/client/pages/memory/MemoryDetailsDrawer.tsx +5 -5
- package/src/client/pages/memory/MemoryPage.tsx +15 -15
- package/src/client/pages/memory/MemoryTimeline.tsx +3 -5
- package/src/client/pages/people/ContributionGrid.tsx +8 -5
- package/src/client/pages/people/PersonProfilePage.tsx +3 -6
- package/src/client/pages/people/ProfilePluginReports.tsx +2 -2
- package/src/client/pages/system/GuardianActivity.tsx +9 -9
- package/src/client/pages/system/PluginBarChart.tsx +4 -6
- package/src/client/pages/system/PluginDetails.tsx +11 -7
- package/src/client/pages/system/PluginPanels.tsx +8 -8
- package/src/client/pages/system/PluginReportRecordSet.tsx +14 -14
- package/src/client/pages/system/PluginReports.tsx +7 -11
- package/src/client/pages/system/SkillInventory.tsx +9 -9
- package/src/client/pages/system/SystemActivity.tsx +1 -1
- package/src/client/pages/system/SystemPage.tsx +3 -6
- package/src/client/pages/system/WorkspaceDetails.tsx +2 -5
- package/src/client/pages/system/WorkspaceEditor.tsx +2 -2
- package/src/client/pages/system/WorkspaceList.tsx +2 -2
- package/src/client/pages/user/PluginUserPage.tsx +3 -3
- package/src/client/styles.ts +5 -4
- package/src/client/types.ts +2 -0
- package/src/tailwind.css +36 -38
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
2
|
+
import { Plug } from "lucide-react";
|
|
3
|
+
import type { z } from "zod";
|
|
4
|
+
import {
|
|
5
|
+
connectionListSchema,
|
|
6
|
+
disconnectConnectionResponseSchema,
|
|
7
|
+
startConnectionResponseSchema,
|
|
8
|
+
type connectionSchema,
|
|
9
|
+
} from "@sentry/junior/api/schema";
|
|
10
|
+
import { del, fetchDashboardJson, post } from "../http";
|
|
11
|
+
import { dashboardContainerClass } from "../styles";
|
|
12
|
+
import { getDashboardAgentName } from "../agentName";
|
|
13
|
+
import { useDocumentTitle } from "../documentTitle";
|
|
14
|
+
import { Button } from "../components/Button";
|
|
15
|
+
import { InlineError } from "../components/InlineError";
|
|
16
|
+
import { Card } from "../components/layout/Card";
|
|
17
|
+
|
|
18
|
+
type Connection = z.infer<typeof connectionSchema>;
|
|
19
|
+
|
|
20
|
+
const connectionsQueryKey = ["dashboard", "connections"] as const;
|
|
21
|
+
|
|
22
|
+
function connectionPath(provider: string): string {
|
|
23
|
+
return `/api/connections/${encodeURIComponent(provider)}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Connect and disconnect the viewer's plugin accounts. */
|
|
27
|
+
export function ConnectionsPage() {
|
|
28
|
+
useDocumentTitle("Connected accounts");
|
|
29
|
+
const queryClient = useQueryClient();
|
|
30
|
+
const connectionsQuery = useQuery({
|
|
31
|
+
queryKey: connectionsQueryKey,
|
|
32
|
+
queryFn: ({ signal }) =>
|
|
33
|
+
fetchDashboardJson(connectionListSchema, "/api/connections", signal),
|
|
34
|
+
// OAuth finishes in another tab. Refresh when the viewer returns.
|
|
35
|
+
refetchOnWindowFocus: true,
|
|
36
|
+
retry: false,
|
|
37
|
+
});
|
|
38
|
+
const connectMutation = useMutation({
|
|
39
|
+
mutationFn: (input: { provider: string; popup: Window | null }) =>
|
|
40
|
+
post(startConnectionResponseSchema, connectionPath(input.provider), {}),
|
|
41
|
+
onSuccess: ({ authorizationUrl }, { popup }) => {
|
|
42
|
+
// A blocked popup returns null, so use this tab. A tab the viewer
|
|
43
|
+
// closed means they cancelled; keep them on the dashboard.
|
|
44
|
+
if (!popup) {
|
|
45
|
+
window.location.assign(authorizationUrl);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (popup.closed) return;
|
|
49
|
+
popup.opener = null;
|
|
50
|
+
popup.location.href = authorizationUrl;
|
|
51
|
+
},
|
|
52
|
+
onError: (_error, { popup }) => {
|
|
53
|
+
popup?.close();
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
const disconnectMutation = useMutation({
|
|
57
|
+
mutationFn: (connection: Connection) =>
|
|
58
|
+
del(
|
|
59
|
+
disconnectConnectionResponseSchema,
|
|
60
|
+
connectionPath(connection.provider),
|
|
61
|
+
),
|
|
62
|
+
onSuccess: (_result, connection) => {
|
|
63
|
+
queryClient.setQueryData<{ connections: Connection[] }>(
|
|
64
|
+
connectionsQueryKey,
|
|
65
|
+
(current) =>
|
|
66
|
+
current && {
|
|
67
|
+
connections: current.connections.map((item) =>
|
|
68
|
+
item.provider === connection.provider
|
|
69
|
+
? {
|
|
70
|
+
provider: item.provider,
|
|
71
|
+
displayName: item.displayName,
|
|
72
|
+
description: item.description,
|
|
73
|
+
connected: false,
|
|
74
|
+
}
|
|
75
|
+
: item,
|
|
76
|
+
),
|
|
77
|
+
},
|
|
78
|
+
);
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
const connections = connectionsQuery.data?.connections ?? [];
|
|
82
|
+
const busy = connectMutation.isPending || disconnectMutation.isPending;
|
|
83
|
+
const pendingProvider = connectMutation.isPending
|
|
84
|
+
? connectMutation.variables?.provider
|
|
85
|
+
: disconnectMutation.isPending
|
|
86
|
+
? disconnectMutation.variables?.provider
|
|
87
|
+
: undefined;
|
|
88
|
+
const listFailed = !connectionsQuery.data && connectionsQuery.isError;
|
|
89
|
+
const displayedError = connectMutation.isError
|
|
90
|
+
? "Could not start the connection. Try again."
|
|
91
|
+
: disconnectMutation.isError
|
|
92
|
+
? "Could not disconnect the account. Try again."
|
|
93
|
+
: listFailed
|
|
94
|
+
? "Could not load connected accounts. Try again."
|
|
95
|
+
: undefined;
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<div className={`${dashboardContainerClass} px-4 py-8 md:px-8`}>
|
|
99
|
+
<section className="mx-auto w-full max-w-3xl">
|
|
100
|
+
<h1 className="m-0 text-2xl font-bold">Connected accounts</h1>
|
|
101
|
+
<p className="mt-2 mb-0 max-w-2xl text-sm text-dashboard-text-muted">
|
|
102
|
+
Connect an account so {getDashboardAgentName()} can act as you in
|
|
103
|
+
conversations you start here.
|
|
104
|
+
</p>
|
|
105
|
+
|
|
106
|
+
<Card className="mb-0 mt-6" padding="md" variant="raised">
|
|
107
|
+
{displayedError ? (
|
|
108
|
+
<InlineError className={listFailed ? undefined : "mb-4"}>
|
|
109
|
+
{displayedError}
|
|
110
|
+
</InlineError>
|
|
111
|
+
) : null}
|
|
112
|
+
{listFailed ? null : (
|
|
113
|
+
<div className="space-y-2">
|
|
114
|
+
{connectionsQuery.isPending ? (
|
|
115
|
+
<p className="m-0 text-sm text-dashboard-text-muted">
|
|
116
|
+
Loading connected accounts…
|
|
117
|
+
</p>
|
|
118
|
+
) : connections.length === 0 ? (
|
|
119
|
+
<p className="m-0 text-sm text-dashboard-text-muted">
|
|
120
|
+
No installed plugins support account connections.
|
|
121
|
+
</p>
|
|
122
|
+
) : (
|
|
123
|
+
connections.map((connection) => (
|
|
124
|
+
<div
|
|
125
|
+
className="flex items-center gap-3 rounded-md border border-dashboard-border-strong p-3"
|
|
126
|
+
key={connection.provider}
|
|
127
|
+
>
|
|
128
|
+
<Plug
|
|
129
|
+
className="shrink-0 text-dashboard-accent"
|
|
130
|
+
size={16}
|
|
131
|
+
/>
|
|
132
|
+
<div className="min-w-0 flex-1">
|
|
133
|
+
<p className="m-0 truncate text-sm font-semibold">
|
|
134
|
+
{connection.displayName}
|
|
135
|
+
</p>
|
|
136
|
+
<p className="mt-1 mb-0 line-clamp-2 break-words text-xs text-dashboard-text-muted">
|
|
137
|
+
{connection.connected ? (
|
|
138
|
+
connection.account ? (
|
|
139
|
+
<>
|
|
140
|
+
Connected as{" "}
|
|
141
|
+
{connection.account.url ? (
|
|
142
|
+
<a
|
|
143
|
+
className="text-dashboard-text"
|
|
144
|
+
href={connection.account.url}
|
|
145
|
+
rel="noreferrer"
|
|
146
|
+
target="_blank"
|
|
147
|
+
>
|
|
148
|
+
{connection.account.label}
|
|
149
|
+
</a>
|
|
150
|
+
) : (
|
|
151
|
+
connection.account.label
|
|
152
|
+
)}
|
|
153
|
+
</>
|
|
154
|
+
) : (
|
|
155
|
+
"Connected"
|
|
156
|
+
)
|
|
157
|
+
) : (
|
|
158
|
+
connection.description
|
|
159
|
+
)}
|
|
160
|
+
</p>
|
|
161
|
+
</div>
|
|
162
|
+
{connection.connected ? (
|
|
163
|
+
<Button
|
|
164
|
+
aria-busy={pendingProvider === connection.provider}
|
|
165
|
+
aria-label={`Disconnect ${connection.displayName}`}
|
|
166
|
+
disabled={busy}
|
|
167
|
+
onClick={() => {
|
|
168
|
+
connectMutation.reset();
|
|
169
|
+
disconnectMutation.mutate(connection);
|
|
170
|
+
}}
|
|
171
|
+
tone="danger"
|
|
172
|
+
>
|
|
173
|
+
{pendingProvider === connection.provider
|
|
174
|
+
? "Disconnecting…"
|
|
175
|
+
: "Disconnect"}
|
|
176
|
+
</Button>
|
|
177
|
+
) : (
|
|
178
|
+
<Button
|
|
179
|
+
aria-busy={pendingProvider === connection.provider}
|
|
180
|
+
aria-label={`Connect ${connection.displayName}`}
|
|
181
|
+
disabled={busy}
|
|
182
|
+
onClick={() => {
|
|
183
|
+
disconnectMutation.reset();
|
|
184
|
+
// Open the tab during the click so popup blockers
|
|
185
|
+
// allow it, then point it at the provider.
|
|
186
|
+
const popup = window.open("about:blank", "_blank");
|
|
187
|
+
connectMutation.mutate({
|
|
188
|
+
provider: connection.provider,
|
|
189
|
+
popup,
|
|
190
|
+
});
|
|
191
|
+
}}
|
|
192
|
+
>
|
|
193
|
+
{pendingProvider === connection.provider
|
|
194
|
+
? "Connecting…"
|
|
195
|
+
: "Connect"}
|
|
196
|
+
</Button>
|
|
197
|
+
)}
|
|
198
|
+
</div>
|
|
199
|
+
))
|
|
200
|
+
)}
|
|
201
|
+
</div>
|
|
202
|
+
)}
|
|
203
|
+
</Card>
|
|
204
|
+
</section>
|
|
205
|
+
</div>
|
|
206
|
+
);
|
|
207
|
+
}
|
|
@@ -110,11 +110,11 @@ export function PersonalTokensPage() {
|
|
|
110
110
|
|
|
111
111
|
<Card className="mb-0 mt-6" padding="md" variant="raised">
|
|
112
112
|
{createdToken ? (
|
|
113
|
-
<div className="mt-5 rounded border border-emerald-400/40 bg-emerald-400/5 p-3">
|
|
113
|
+
<div className="mt-5 rounded-md border border-emerald-400/40 bg-emerald-400/5 p-3">
|
|
114
114
|
<p className="mt-0 mb-2 text-sm font-semibold">
|
|
115
115
|
Copy this token now. It won't be shown again.
|
|
116
116
|
</p>
|
|
117
|
-
<code className="block overflow-x-auto rounded bg-
|
|
117
|
+
<code className="block overflow-x-auto rounded-md bg-dashboard-ink p-2 text-xs text-emerald-300">
|
|
118
118
|
{createdToken.token}
|
|
119
119
|
</code>
|
|
120
120
|
<Button
|
|
@@ -166,10 +166,10 @@ export function PersonalTokensPage() {
|
|
|
166
166
|
) : (
|
|
167
167
|
tokens.map((token) => (
|
|
168
168
|
<div
|
|
169
|
-
className="flex items-center gap-3 rounded border border-
|
|
169
|
+
className="flex items-center gap-3 rounded-md border border-dashboard-border-strong p-3"
|
|
170
170
|
key={token.id}
|
|
171
171
|
>
|
|
172
|
-
<KeyRound className="text-
|
|
172
|
+
<KeyRound className="text-dashboard-accent" size={16} />
|
|
173
173
|
<div className="min-w-0 flex-1">
|
|
174
174
|
<p className="m-0 truncate text-sm font-semibold">
|
|
175
175
|
{token.name}
|
|
@@ -31,7 +31,7 @@ export function AdminPage(props: { pages: PluginUserPageLink[] }) {
|
|
|
31
31
|
{adminPages.length === 0 ? (
|
|
32
32
|
<Card padding="md">
|
|
33
33
|
<div className="flex items-center gap-4">
|
|
34
|
-
<div className="grid size-10 shrink-0 place-items-center rounded border border-
|
|
34
|
+
<div className="grid size-10 shrink-0 place-items-center rounded-md border border-dashboard-border bg-dashboard-fill-soft text-dashboard-text-muted">
|
|
35
35
|
<Boxes aria-hidden="true" size={17} />
|
|
36
36
|
</div>
|
|
37
37
|
<p className="m-0 text-sm text-dashboard-text-muted">
|
|
@@ -50,7 +50,7 @@ export function AdminPage(props: { pages: PluginUserPageLink[] }) {
|
|
|
50
50
|
<Card padding="md">
|
|
51
51
|
<div className="flex items-center gap-3">
|
|
52
52
|
<div className="min-w-0 flex-1">
|
|
53
|
-
<div className="font-
|
|
53
|
+
<div className="font-sans font-medium text-xs text-dashboard-text-muted">
|
|
54
54
|
{page.pluginDisplayName}
|
|
55
55
|
</div>
|
|
56
56
|
<h2 className="mt-1 mb-0 font-display text-base font-medium text-dashboard-text">
|
|
@@ -45,7 +45,7 @@ export function AutomationCostChart(props: {
|
|
|
45
45
|
<h2 className="m-0 font-display text-xl font-medium text-dashboard-text">
|
|
46
46
|
{formatCostSummary({ total }) || "$0.00"}
|
|
47
47
|
</h2>
|
|
48
|
-
<p className="mt-1 mb-0 font-
|
|
48
|
+
<p className="mt-1 mb-0 font-sans text-xs leading-relaxed text-dashboard-text-muted">
|
|
49
49
|
Spend from conversations linked to completed automation executions.
|
|
50
50
|
</p>
|
|
51
51
|
</div>
|
|
@@ -112,7 +112,7 @@ export function AutomationCostChart(props: {
|
|
|
112
112
|
/>
|
|
113
113
|
</ChartSvg>
|
|
114
114
|
{!hasCost ? (
|
|
115
|
-
<div className="pointer-events-none absolute inset-0 grid place-items-center pt-12 font-
|
|
115
|
+
<div className="pointer-events-none absolute inset-0 grid place-items-center pt-12 font-sans text-xs text-dashboard-text-muted">
|
|
116
116
|
No linked spend in this period.
|
|
117
117
|
</div>
|
|
118
118
|
) : null}
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
import { conversationPath } from "../../conversations/conversationRoutes";
|
|
18
18
|
import { formatTime, peoplePath } from "../../format";
|
|
19
19
|
import { TranscriptText } from "../../conversations/TranscriptText";
|
|
20
|
+
import { cn, dashboardInlineLinkClass } from "../../styles";
|
|
20
21
|
|
|
21
22
|
/** Show one automation's instruction and metadata in a right-side slide-out. */
|
|
22
23
|
export function AutomationDetailsDrawer(props: {
|
|
@@ -30,7 +31,7 @@ export function AutomationDetailsDrawer(props: {
|
|
|
30
31
|
const { automation } = props;
|
|
31
32
|
const createdBy = automation.createdByEmail ? (
|
|
32
33
|
<Link
|
|
33
|
-
className="font-semibold
|
|
34
|
+
className={cn("font-semibold", dashboardInlineLinkClass)}
|
|
34
35
|
to={peoplePath(automation.createdByEmail)}
|
|
35
36
|
>
|
|
36
37
|
{automation.ownedByViewer ? "you" : automation.createdBy}
|
|
@@ -175,10 +176,7 @@ function AutomationExecutionSummary(props: {
|
|
|
175
176
|
return (
|
|
176
177
|
<div className="text-sm text-dashboard-text-muted">
|
|
177
178
|
{automation.totalRuns > 0 ? (
|
|
178
|
-
<Link
|
|
179
|
-
className="text-dashboard-text underline decoration-white/20 underline-offset-2 hover:decoration-white/60"
|
|
180
|
-
to={executionsPath}
|
|
181
|
-
>
|
|
179
|
+
<Link className={dashboardInlineLinkClass} to={executionsPath}>
|
|
182
180
|
{runCount} runs / {rangeLabel}
|
|
183
181
|
</Link>
|
|
184
182
|
) : (
|
|
@@ -189,10 +187,7 @@ function AutomationExecutionSummary(props: {
|
|
|
189
187
|
<span className="mx-2 opacity-45">·</span>
|
|
190
188
|
<span>
|
|
191
189
|
{automation.totalRuns > 0 ? (
|
|
192
|
-
<Link
|
|
193
|
-
className="text-dashboard-text underline decoration-white/20 underline-offset-2 hover:decoration-white/60"
|
|
194
|
-
to={executionsPath}
|
|
195
|
-
>
|
|
190
|
+
<Link className={dashboardInlineLinkClass} to={executionsPath}>
|
|
196
191
|
{automation.totalRuns} total
|
|
197
192
|
</Link>
|
|
198
193
|
) : (
|
|
@@ -201,7 +196,7 @@ function AutomationExecutionSummary(props: {
|
|
|
201
196
|
{automation.lastRunAt ? " · Last run " : " · Never run"}
|
|
202
197
|
{automation.lastRunAt && automation.lastConversationId ? (
|
|
203
198
|
<Link
|
|
204
|
-
className=
|
|
199
|
+
className={dashboardInlineLinkClass}
|
|
205
200
|
to={conversationPath(automation.lastConversationId)}
|
|
206
201
|
>
|
|
207
202
|
{formatAutomationRunTime(automation.lastRunAt)}
|
|
@@ -425,7 +425,7 @@ export function AutomationEditor(props: {
|
|
|
425
425
|
<div className="flex shrink-0 gap-2">
|
|
426
426
|
<Link
|
|
427
427
|
to={props.returnPath}
|
|
428
|
-
className="inline-flex min-h-11 items-center rounded border border-dashboard-border-emphasis px-3 text-sm text-dashboard-text no-underline sm:min-h-9"
|
|
428
|
+
className="inline-flex min-h-11 items-center rounded-md border border-dashboard-border-emphasis px-3 text-sm text-dashboard-text no-underline sm:min-h-9"
|
|
429
429
|
>
|
|
430
430
|
Cancel
|
|
431
431
|
</Link>
|
|
@@ -45,7 +45,7 @@ export function AutomationExecutionChart(props: {
|
|
|
45
45
|
<h2 className="m-0 font-display text-xl font-medium text-dashboard-text">
|
|
46
46
|
{totals.reduce((sum, value) => sum + value, 0).toLocaleString()} runs
|
|
47
47
|
</h2>
|
|
48
|
-
<p className="mt-1 mb-0 font-
|
|
48
|
+
<p className="mt-1 mb-0 font-sans text-xs leading-relaxed text-dashboard-text-muted">
|
|
49
49
|
Completed automation executions over time.
|
|
50
50
|
</p>
|
|
51
51
|
</div>
|
|
@@ -104,7 +104,7 @@ export function AutomationExecutionChart(props: {
|
|
|
104
104
|
/>
|
|
105
105
|
</ChartSvg>
|
|
106
106
|
{!hasExecutions ? (
|
|
107
|
-
<div className="pointer-events-none absolute inset-0 grid place-items-center pt-12 font-
|
|
107
|
+
<div className="pointer-events-none absolute inset-0 grid place-items-center pt-12 font-sans text-xs text-dashboard-text-muted">
|
|
108
108
|
No automation executions in this period.
|
|
109
109
|
</div>
|
|
110
110
|
) : null}
|
|
@@ -44,7 +44,7 @@ export function AutomationExecutionStatusChart(props: {
|
|
|
44
44
|
<h2 className="m-0 font-display text-xl font-medium text-dashboard-text">
|
|
45
45
|
Executions over time
|
|
46
46
|
</h2>
|
|
47
|
-
<p className="mt-1 mb-0 font-
|
|
47
|
+
<p className="mt-1 mb-0 font-sans text-xs leading-relaxed text-dashboard-text-muted">
|
|
48
48
|
{bucketUnit === "hour"
|
|
49
49
|
? "Terminal runs for this automation each hour."
|
|
50
50
|
: bucketUnit === "6hour"
|
|
@@ -121,7 +121,7 @@ export function AutomationExecutionStatusChart(props: {
|
|
|
121
121
|
/>
|
|
122
122
|
</ChartSvg>
|
|
123
123
|
{!hasExecutions ? (
|
|
124
|
-
<div className="pointer-events-none absolute inset-0 grid place-items-center pt-12 font-
|
|
124
|
+
<div className="pointer-events-none absolute inset-0 grid place-items-center pt-12 font-sans text-xs text-dashboard-text-muted">
|
|
125
125
|
No automation executions in this period.
|
|
126
126
|
</div>
|
|
127
127
|
) : null}
|
|
@@ -87,7 +87,7 @@ export function AutomationExecutionsPage(props: { enabled: boolean }) {
|
|
|
87
87
|
: "Automation executions could not be loaded. Try again."}
|
|
88
88
|
</InlineError>
|
|
89
89
|
<Link
|
|
90
|
-
className="mt-3 inline-flex items-center gap-2 font-
|
|
90
|
+
className="mt-3 inline-flex items-center gap-2 font-sans text-xs text-dashboard-text-muted no-underline hover:text-dashboard-text"
|
|
91
91
|
to={
|
|
92
92
|
automationId ? automationPath(automationId) : "/automations/list"
|
|
93
93
|
}
|
|
@@ -122,7 +122,7 @@ function AutomationExecutionsView(props: {
|
|
|
122
122
|
<>
|
|
123
123
|
<div>
|
|
124
124
|
<Link
|
|
125
|
-
className="mb-3 inline-flex items-center gap-2 font-
|
|
125
|
+
className="mb-3 inline-flex items-center gap-2 font-sans text-xs text-dashboard-text-muted no-underline hover:text-dashboard-text"
|
|
126
126
|
to={props.backTo}
|
|
127
127
|
>
|
|
128
128
|
<ArrowLeft aria-hidden="true" size={14} />
|
|
@@ -177,7 +177,7 @@ function AutomationExecutionsView(props: {
|
|
|
177
177
|
<div className="min-w-[36rem]">
|
|
178
178
|
<div
|
|
179
179
|
className={cn(
|
|
180
|
-
"sticky top-0 z-[1] hidden items-center gap-4 border-b border-dashboard-border-subtle bg-dashboard-overlay-soft px-4 py-2.5 font-
|
|
180
|
+
"sticky top-0 z-[1] hidden items-center gap-4 border-b border-dashboard-border-subtle bg-dashboard-overlay-soft px-4 py-2.5 font-sans font-medium text-xs text-dashboard-text-muted md:grid",
|
|
181
181
|
EXECUTION_GRID,
|
|
182
182
|
)}
|
|
183
183
|
role="row"
|
|
@@ -290,7 +290,7 @@ function MetricCell(props: { label: string; value: string }) {
|
|
|
290
290
|
<div className="min-w-0 max-md:flex max-md:items-center max-md:gap-2">
|
|
291
291
|
<div
|
|
292
292
|
aria-hidden="true"
|
|
293
|
-
className="mb-1 hidden font-
|
|
293
|
+
className="mb-1 hidden font-sans font-medium text-2xs text-dashboard-text-muted max-md:mb-0 max-md:block"
|
|
294
294
|
>
|
|
295
295
|
{props.label}
|
|
296
296
|
</div>
|
|
@@ -58,7 +58,7 @@ export function AutomationRow(props: {
|
|
|
58
58
|
<button
|
|
59
59
|
aria-expanded={props.selected}
|
|
60
60
|
aria-label={`View automation details: ${automation.title}`}
|
|
61
|
-
className="block w-full min-w-0 cursor-pointer rounded border-0 bg-transparent p-0 text-left focus-visible:outline focus-visible:outline-2 focus-visible:outline-dashboard-focus"
|
|
61
|
+
className="block w-full min-w-0 cursor-pointer rounded-md border-0 bg-transparent p-0 text-left focus-visible:outline focus-visible:outline-2 focus-visible:outline-dashboard-focus"
|
|
62
62
|
onClick={props.onSelect}
|
|
63
63
|
type="button"
|
|
64
64
|
>
|
|
@@ -88,7 +88,7 @@ export function AutomationRow(props: {
|
|
|
88
88
|
<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">
|
|
89
89
|
{automation.createdByEmail ? (
|
|
90
90
|
<Link
|
|
91
|
-
className="inline-flex min-w-0 max-w-full items-center gap-2 rounded text-dashboard-text no-underline hover:underline focus-visible:outline focus-visible:outline-2 focus-visible:outline-dashboard-focus"
|
|
91
|
+
className="inline-flex min-w-0 max-w-full items-center gap-2 rounded-md text-dashboard-text no-underline hover:underline focus-visible:outline focus-visible:outline-2 focus-visible:outline-dashboard-focus"
|
|
92
92
|
title={automation.createdBy}
|
|
93
93
|
to={peoplePath(automation.createdByEmail)}
|
|
94
94
|
>
|
|
@@ -320,12 +320,12 @@ function AutomationActions(props: {
|
|
|
320
320
|
<Link
|
|
321
321
|
role="menuitem"
|
|
322
322
|
to={props.editPath}
|
|
323
|
-
className="flex min-h-11 items-center rounded px-3 text-sm text-dashboard-text no-underline hover:bg-dashboard-fill-hover focus-visible:outline focus-visible:outline-dashboard-focus"
|
|
323
|
+
className="flex min-h-11 items-center rounded-md px-3 text-sm text-dashboard-text no-underline hover:bg-dashboard-fill-hover focus-visible:outline focus-visible:outline-dashboard-focus"
|
|
324
324
|
>
|
|
325
325
|
Edit automation
|
|
326
326
|
</Link>
|
|
327
327
|
<button
|
|
328
|
-
className="flex min-h-11 w-full cursor-pointer items-center gap-2 rounded border-0 bg-transparent px-3 text-sm text-rose-300 hover:bg-dashboard-fill-hover focus-visible:bg-dashboard-fill-hover focus-visible:outline focus-visible:outline-2 focus-visible:outline-dashboard-focus"
|
|
328
|
+
className="flex min-h-11 w-full cursor-pointer items-center gap-2 rounded-md border-0 bg-transparent px-3 text-sm text-rose-300 hover:bg-dashboard-fill-hover focus-visible:bg-dashboard-fill-hover focus-visible:outline focus-visible:outline-2 focus-visible:outline-dashboard-focus"
|
|
329
329
|
onClick={() => {
|
|
330
330
|
close();
|
|
331
331
|
props.onDelete();
|
|
@@ -156,7 +156,7 @@ export function AutomationRunsPage(props: { enabled: boolean }) {
|
|
|
156
156
|
<div className="min-w-[44rem]">
|
|
157
157
|
<div
|
|
158
158
|
className={cn(
|
|
159
|
-
"sticky top-0 z-[1] hidden items-center gap-4 border-b border-dashboard-border-subtle bg-dashboard-overlay-soft px-4 py-2.5 font-
|
|
159
|
+
"sticky top-0 z-[1] hidden items-center gap-4 border-b border-dashboard-border-subtle bg-dashboard-overlay-soft px-4 py-2.5 font-sans font-medium text-xs text-dashboard-text-muted md:grid",
|
|
160
160
|
RUN_GRID,
|
|
161
161
|
)}
|
|
162
162
|
role="row"
|
|
@@ -278,7 +278,7 @@ function MetricCell(props: { label: string; value: string }) {
|
|
|
278
278
|
<div className="min-w-0 max-md:flex max-md:items-center max-md:gap-2">
|
|
279
279
|
<div
|
|
280
280
|
aria-hidden="true"
|
|
281
|
-
className="mb-1 hidden font-
|
|
281
|
+
className="mb-1 hidden font-sans font-medium text-2xs text-dashboard-text-muted max-md:mb-0 max-md:block"
|
|
282
282
|
>
|
|
283
283
|
{props.label}
|
|
284
284
|
</div>
|
|
@@ -297,7 +297,7 @@ function AutomationKindIcon(props: { kind: AutomationRun["kind"] }) {
|
|
|
297
297
|
return (
|
|
298
298
|
<span
|
|
299
299
|
aria-label={label}
|
|
300
|
-
className="inline-flex size-3.5 shrink-0 items-center justify-center text-
|
|
300
|
+
className="inline-flex size-3.5 shrink-0 items-center justify-center text-dashboard-accent/80"
|
|
301
301
|
role="img"
|
|
302
302
|
title={label}
|
|
303
303
|
>
|
|
@@ -152,7 +152,7 @@ export function AutomationScheduleFields(props: {
|
|
|
152
152
|
})
|
|
153
153
|
}
|
|
154
154
|
className={cn(
|
|
155
|
-
"min-h-11 cursor-pointer rounded border px-1 text-xs font-medium capitalize focus-visible:outline-2 focus-visible:outline-dashboard-focus sm:min-h-10 sm:text-sm",
|
|
155
|
+
"min-h-11 cursor-pointer rounded-md border px-1 text-xs font-medium capitalize focus-visible:outline-2 focus-visible:outline-dashboard-focus sm:min-h-10 sm:text-sm",
|
|
156
156
|
value.weekdays?.includes(day)
|
|
157
157
|
? "border-dashboard-focus/50 bg-dashboard-focus/10 text-dashboard-focus"
|
|
158
158
|
: "border-dashboard-border-emphasis text-dashboard-text-muted hover:bg-dashboard-fill-hover",
|
|
@@ -77,7 +77,7 @@ export function AutomationVersionsPage(props: { enabled: boolean }) {
|
|
|
77
77
|
function BackLink(props: { to: string }) {
|
|
78
78
|
return (
|
|
79
79
|
<Link
|
|
80
|
-
className="mb-3 inline-flex items-center gap-2 font-
|
|
80
|
+
className="mb-3 inline-flex items-center gap-2 font-sans text-xs text-dashboard-text-muted no-underline hover:text-dashboard-text"
|
|
81
81
|
to={props.to}
|
|
82
82
|
>
|
|
83
83
|
<ArrowLeft aria-hidden="true" size={14} />
|
|
@@ -40,7 +40,10 @@ export function CodeActivityChart(props: {
|
|
|
40
40
|
const gap = Math.max(1, barWidth * 0.2);
|
|
41
41
|
const groupWidth = barWidth * 3 + gap * 2;
|
|
42
42
|
const totals = days.map((day) => day.created + day.merged + day.closed);
|
|
43
|
-
const maximum = Math.max(
|
|
43
|
+
const maximum = Math.max(
|
|
44
|
+
1,
|
|
45
|
+
...days.flatMap((day) => [day.created, day.merged, day.closed]),
|
|
46
|
+
);
|
|
44
47
|
const average = activityChartAverage(days.map((day) => day.created));
|
|
45
48
|
const hasActivity = totals.some((total) => total > 0);
|
|
46
49
|
|
|
@@ -51,7 +54,7 @@ export function CodeActivityChart(props: {
|
|
|
51
54
|
<h2 className="m-0 font-display text-xl font-medium text-dashboard-text">
|
|
52
55
|
Changes over time
|
|
53
56
|
</h2>
|
|
54
|
-
<p className="mt-1 mb-0 font-
|
|
57
|
+
<p className="mt-1 mb-0 font-sans text-xs leading-relaxed text-dashboard-text-muted">
|
|
55
58
|
Code changes created, merged, and closed each day.
|
|
56
59
|
</p>
|
|
57
60
|
</div>
|
|
@@ -106,9 +109,7 @@ export function CodeActivityChart(props: {
|
|
|
106
109
|
date={day.date}
|
|
107
110
|
summary={`${day.created} created, ${day.merged} merged, ${day.closed} closed`}
|
|
108
111
|
>
|
|
109
|
-
<g
|
|
110
|
-
tabIndex={0}
|
|
111
|
-
>
|
|
112
|
+
<g tabIndex={0}>
|
|
112
113
|
{series.map((entry, seriesIndex) => {
|
|
113
114
|
const height = (entry.value / maximum) * layout.plotHeight;
|
|
114
115
|
const x = groupX + seriesIndex * (barWidth + gap);
|
|
@@ -151,7 +152,7 @@ export function CodeActivityChart(props: {
|
|
|
151
152
|
/>
|
|
152
153
|
</ChartSvg>
|
|
153
154
|
{!hasActivity ? (
|
|
154
|
-
<div className="pointer-events-none absolute inset-0 grid place-items-center pt-12 font-
|
|
155
|
+
<div className="pointer-events-none absolute inset-0 grid place-items-center pt-12 font-sans text-xs text-dashboard-text-muted">
|
|
155
156
|
No code changes in this period.
|
|
156
157
|
</div>
|
|
157
158
|
) : null}
|
|
@@ -18,7 +18,7 @@ export function CodeChangeRow(props: { change: CodeChangeSummaryReport }) {
|
|
|
18
18
|
<div className="truncate font-display text-sm text-dashboard-text">
|
|
19
19
|
{change.url ? (
|
|
20
20
|
<a
|
|
21
|
-
className="text-inherit no-underline hover:text-
|
|
21
|
+
className="text-inherit no-underline hover:text-dashboard-accent-text"
|
|
22
22
|
href={change.url}
|
|
23
23
|
rel="noreferrer"
|
|
24
24
|
target="_blank"
|
|
@@ -96,7 +96,7 @@ function CodeOverview(props: {
|
|
|
96
96
|
<col className="w-28" />
|
|
97
97
|
<col className="w-28" />
|
|
98
98
|
</colgroup>
|
|
99
|
-
<thead className="font-
|
|
99
|
+
<thead className="font-sans font-medium text-xs text-dashboard-text-muted">
|
|
100
100
|
<tr className="border-b border-dashboard-border-subtle">
|
|
101
101
|
<th className="px-4 py-2.5 font-medium">Repository</th>
|
|
102
102
|
<th className="px-4 py-2.5 text-right font-medium whitespace-nowrap">
|
|
@@ -119,7 +119,7 @@ function CodeOverview(props: {
|
|
|
119
119
|
<td className="min-w-0 px-4 py-3">
|
|
120
120
|
<div className="truncate font-display text-sm text-dashboard-text">
|
|
121
121
|
<Link
|
|
122
|
-
className="text-inherit no-underline hover:text-
|
|
122
|
+
className="text-inherit no-underline hover:text-dashboard-accent-text"
|
|
123
123
|
to={codeRepositoryPath(repository.id)}
|
|
124
124
|
>
|
|
125
125
|
{repository.name}
|
|
@@ -129,13 +129,13 @@ function CodeOverview(props: {
|
|
|
129
129
|
{repository.provider}
|
|
130
130
|
</div>
|
|
131
131
|
</td>
|
|
132
|
-
<td className="px-4 py-3 text-right font-
|
|
132
|
+
<td className="px-4 py-3 text-right font-sans text-sm tabular-nums whitespace-nowrap text-dashboard-text">
|
|
133
133
|
{repository.created}
|
|
134
134
|
</td>
|
|
135
|
-
<td className="px-4 py-3 text-right font-
|
|
135
|
+
<td className="px-4 py-3 text-right font-sans text-sm tabular-nums whitespace-nowrap text-dashboard-text">
|
|
136
136
|
{formatMergeRate(repository.mergeRate)}
|
|
137
137
|
</td>
|
|
138
|
-
<td className="px-4 py-3 text-right font-
|
|
138
|
+
<td className="px-4 py-3 text-right font-sans text-sm tabular-nums whitespace-nowrap text-dashboard-text">
|
|
139
139
|
{formatCodeCost(repository.medianCostUsd)}
|
|
140
140
|
</td>
|
|
141
141
|
</tr>
|
|
@@ -35,13 +35,13 @@ import {
|
|
|
35
35
|
RepositoryChangesTab,
|
|
36
36
|
RepositoryConversationsTab,
|
|
37
37
|
} from "./RepositoryTabs";
|
|
38
|
+
import { cn, dashboardInlineLinkClass } from "../../styles";
|
|
38
39
|
|
|
39
40
|
const RECENT_ROWS = 5;
|
|
40
41
|
const DESCRIPTION = "Conversations and code changes from Junior.";
|
|
41
42
|
|
|
42
43
|
/** Inline link style used by `ConversationSummary` location links. */
|
|
43
|
-
const inlineLinkClass =
|
|
44
|
-
"font-semibold text-dashboard-text underline decoration-white/20 underline-offset-2 transition-colors hover:decoration-white/60";
|
|
44
|
+
const inlineLinkClass = cn("font-semibold", dashboardInlineLinkClass);
|
|
45
45
|
|
|
46
46
|
/** Render one repository tab under the shared header and tab bar. */
|
|
47
47
|
export function RepositoryPage(props: { tab: CodeRepositoryTab }) {
|