Skip to content

Commit 798fdf9

Browse files
committed
refactor(webapp): split the dashboard agent's UI out of the first PR
The system — contracts, storage, auth, the agent package and its webapp routes — lands first; the panel, the page-context marks and the entry points follow in their own PR.
1 parent 49051f5 commit 798fdf9

139 files changed

Lines changed: 981 additions & 11402 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/webapp/app/components/BlankStatePanels.tsx

Lines changed: 47 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434
v3NewProjectAlertPath,
3535
v3NewSchedulePath,
3636
} from "~/utils/pathBuilder";
37+
import { AskAI } from "./AskAI";
3738
import { CodeBlock } from "./code/CodeBlock";
3839
import { InlineCode } from "./code/InlineCode";
3940
import { environmentFullTitle, EnvironmentIcon } from "./environments/EnvironmentLabel";
@@ -61,35 +62,6 @@ import {
6162
import { StepContentContainer } from "./StepContentContainer";
6263
import { V4Badge } from "./V4Badge";
6364

64-
function DeployDocsLinks() {
65-
return (
66-
<>
67-
<SimpleTooltip
68-
button={
69-
<LinkButton
70-
variant="small-menu-item"
71-
LeadingIcon={BookOpenIcon}
72-
leadingIconClassName="text-blue-500"
73-
to={docsPath("deployment/overview")}
74-
/>
75-
}
76-
content="Deploy docs"
77-
/>
78-
<SimpleTooltip
79-
button={
80-
<LinkButton
81-
variant="small-menu-item"
82-
LeadingIcon={QuestionMarkCircleIcon}
83-
leadingIconClassName="text-blue-500"
84-
to={docsPath("troubleshooting#deployment")}
85-
/>
86-
}
87-
content="Troubleshooting docs"
88-
/>
89-
</>
90-
);
91-
}
92-
9365
export function HasNoTasksDev() {
9466
return (
9567
<PackageManagerProvider>
@@ -298,7 +270,29 @@ export function DeploymentsNoneDev() {
298270
<Header1>Deploy your tasks</Header1>
299271
</div>
300272
<div className="flex items-center">
301-
<DeployDocsLinks />
273+
<SimpleTooltip
274+
button={
275+
<LinkButton
276+
variant="small-menu-item"
277+
LeadingIcon={BookOpenIcon}
278+
leadingIconClassName="text-blue-500"
279+
to={docsPath("deployment/overview")}
280+
/>
281+
}
282+
content="Deploy docs"
283+
/>
284+
<SimpleTooltip
285+
button={
286+
<LinkButton
287+
variant="small-menu-item"
288+
LeadingIcon={QuestionMarkCircleIcon}
289+
leadingIconClassName="text-blue-500"
290+
to={docsPath("troubleshooting#deployment")}
291+
/>
292+
}
293+
content="Troubleshooting docs"
294+
/>
295+
<AskAI />
302296
</div>
303297
</div>
304298
<StepNumber stepNumber="→" title="Switch to a deployed environment" />
@@ -664,7 +658,29 @@ function DeploymentOnboardingSteps() {
664658
</Header1>
665659
</div>
666660
<div className="flex items-center">
667-
<DeployDocsLinks />
661+
<SimpleTooltip
662+
button={
663+
<LinkButton
664+
variant="small-menu-item"
665+
LeadingIcon={BookOpenIcon}
666+
leadingIconClassName="text-blue-500"
667+
to={docsPath("deployment/overview")}
668+
/>
669+
}
670+
content="Deploy docs"
671+
/>
672+
<SimpleTooltip
673+
button={
674+
<LinkButton
675+
variant="small-menu-item"
676+
LeadingIcon={QuestionMarkCircleIcon}
677+
leadingIconClassName="text-blue-500"
678+
to={docsPath("troubleshooting#deployment")}
679+
/>
680+
}
681+
content="Troubleshooting docs"
682+
/>
683+
<AskAI />
668684
</div>
669685
</div>
670686
<ClientTabs defaultValue="github">

apps/webapp/app/components/Shortcuts.tsx

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
import { KeyboardIcon } from "~/assets/icons/KeyboardIcon";
22
import { useState } from "react";
3-
import { ASK_AGENT_LABEL } from "~/components/dashboard-agent/agent-identity";
4-
import { NEW_CHAT_SHORTCUT } from "~/components/dashboard-agent/DashboardAgentHeader";
5-
import { TOGGLE_PANEL_SHORTCUT } from "~/components/dashboard-agent/dashboardAgentLauncher";
63
import { useShortcutKeys } from "~/hooks/useShortcutKeys";
74
import { Header3 } from "./primitives/Headers";
85
import { SideMenuItemButton } from "./navigation/SideMenuItem";
@@ -65,9 +62,9 @@ function ShortcutContent() {
6562
<ShortcutKey shortcut={{ modifiers: ["mod"] }} variant="medium/bright" />
6663
<ShortcutKey shortcut={{ key: "enter" }} variant="medium/bright" />
6764
</Shortcut>
68-
<Shortcut name={ASK_AGENT_LABEL}>
65+
<Shortcut name="Ask AI">
6966
<ShortcutKey shortcut={{ modifiers: ["mod"] }} variant="medium/bright" />
70-
<ShortcutKey shortcut={{ key: TOGGLE_PANEL_SHORTCUT.key }} variant="medium/bright" />
67+
<ShortcutKey shortcut={{ key: "i" }} variant="medium/bright" />
7168
</Shortcut>
7269
<Shortcut name="Filter">
7370
<ShortcutKey shortcut={{ key: "f" }} variant="medium/bright" />
@@ -97,19 +94,6 @@ function ShortcutContent() {
9794
<ShortcutKey shortcut={{ key: "h" }} variant="medium/bright" />
9895
</Shortcut>
9996
</div>
100-
<div className="space-y-3">
101-
<Header3>Chat</Header3>
102-
<Shortcut name="New chat">
103-
<ShortcutKey
104-
shortcut={{ modifiers: NEW_CHAT_SHORTCUT.modifiers }}
105-
variant="medium/bright"
106-
/>
107-
<ShortcutKey shortcut={{ key: NEW_CHAT_SHORTCUT.key }} variant="medium/bright" />
108-
</Shortcut>
109-
<Shortcut name="Close chat">
110-
<ShortcutKey shortcut={{ key: "esc" }} variant="medium/bright" />
111-
</Shortcut>
112-
</div>
11397
<div className="space-y-3">
11498
<Header3>Runs page</Header3>
11599
<Shortcut name="Bulk action: Cancel runs">

apps/webapp/app/components/dashboard-agent/ActionsBlock.tsx

Lines changed: 0 additions & 31 deletions
This file was deleted.
Lines changed: 21 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
import type { OutputColumnMetadata } from "@internal/clickhouse";
22
import type { ChartBlock } from "@internal/dashboard-agent";
3-
import type { AgentIntent, ChartAction } from "@internal/dashboard-agent-contracts";
43
import { useEffect, useState } from "react";
54
import { QueryResultsChart } from "~/components/code/QueryResultsChart";
65
import type { ChartConfiguration } from "~/components/metrics/QueryWidget";
7-
import { Button } from "~/components/primitives/Buttons";
8-
import { AgentSpinner } from "~/components/primitives/Spinner";
6+
import { Spinner } from "~/components/primitives/Spinner";
97
import { useOptionalEnvironment } from "~/hooks/useEnvironment";
108
import { useOptionalOrganization } from "~/hooks/useOrganizations";
119
import { useOptionalProject } from "~/hooks/useProject";
12-
import { cn } from "~/utils/cn";
13-
import { AgentCard, AgentCardHeader } from "./agent-card";
14-
import { ChatActionsRow } from "./chat-layout";
15-
import { renderableActions } from "./view-actions";
10+
11+
// Render an agent "chart" block by running its TRQL query through the dashboard's
12+
// own /resources/metric endpoint (session-authed, returns rows + real column
13+
// metadata) and feeding the result into QueryResultsChart. So the chart is live
14+
// and matches the Query page exactly: the agent only emits the query + chart
15+
// config, never the rows. Runs against the project/env the panel is open in.
1616

1717
type MetricResponse =
1818
| { success: false; error: string }
@@ -25,18 +25,6 @@ type MetricResponse =
2525
};
2626
};
2727

28-
// `chartBlockBodySchema` carries only `period`, so scope and from/to are fixed here.
29-
const CHART_SCOPE = "environment";
30-
const CHART_FROM = null;
31-
const CHART_TO = null;
32-
// `min-h` as well: the chart draws nothing at zero height if a flex parent collapses it.
33-
const CHART_HEIGHT_CLASS = "h-64 min-h-64";
34-
const CHART_PADDING_CLASS = "px-2 pb-2 pt-4";
35-
export const AGENT_CHART_PLOT_CLASS = `w-full ${CHART_PADDING_CLASS} ${CHART_HEIGHT_CLASS}`;
36-
37-
// Query errors can carry SQL and schema detail, so the real one only goes to the console.
38-
const CHART_ERROR_MESSAGE = "This chart's query couldn't run.";
39-
4028
type ChartState =
4129
| { status: "loading" }
4230
| { status: "error"; error: string }
@@ -47,39 +35,7 @@ type ChartState =
4735
timeRange?: { from: string; to: string };
4836
};
4937

50-
export function ChartActions({
51-
actions,
52-
onIntent,
53-
}: {
54-
actions: ChartAction[];
55-
onIntent?: (intent: AgentIntent) => void;
56-
}) {
57-
const renderable = renderableActions(actions);
58-
if (!onIntent || renderable.length === 0) return null;
59-
return (
60-
<div className="border-t border-grid-bright px-2 pb-2 pt-2">
61-
<ChatActionsRow>
62-
{renderable.map((action, i) => (
63-
<Button
64-
key={i}
65-
variant={i === 0 ? "primary/small" : "secondary/small"}
66-
onClick={() => onIntent(action.intent as AgentIntent)}
67-
>
68-
{action.label}
69-
</Button>
70-
))}
71-
</ChatActionsRow>
72-
</div>
73-
);
74-
}
75-
76-
export function AgentChart({
77-
block,
78-
onIntent,
79-
}: {
80-
block: ChartBlock;
81-
onIntent?: (intent: AgentIntent) => void;
82-
}) {
38+
export function AgentChart({ block }: { block: ChartBlock }) {
8339
const organization = useOptionalOrganization();
8440
const project = useOptionalProject();
8541
const environment = useOptionalEnvironment();
@@ -90,7 +46,8 @@ export function AgentChart({
9046
const environmentId = environment?.id;
9147

9248
useEffect(() => {
93-
// The block can render before `query` has streamed in; an empty query 400s.
49+
// The block can render before its `query` has finished streaming in; wait
50+
// for it rather than POST an empty query (which 400s).
9451
if (!block.query) return;
9552
if (!organizationId || !projectId || !environmentId) {
9653
setState({ status: "error", error: "No environment context to run the query." });
@@ -106,10 +63,10 @@ export function AgentChart({
10663
organizationId,
10764
projectId,
10865
environmentId,
109-
scope: CHART_SCOPE,
66+
scope: "environment",
11067
period: block.period ?? null,
111-
from: CHART_FROM,
112-
to: CHART_TO,
68+
from: null,
69+
to: null,
11370
userAuthoredQuery: true,
11471
}),
11572
signal: controller.signal,
@@ -118,8 +75,7 @@ export function AgentChart({
11875
.then((data) => {
11976
if (controller.signal.aborted) return;
12077
if (!data.success) {
121-
console.error("Dashboard agent chart query failed:", data.error);
122-
setState({ status: "error", error: CHART_ERROR_MESSAGE });
78+
setState({ status: "error", error: data.error });
12379
} else {
12480
setState({
12581
status: "ready",
@@ -131,8 +87,7 @@ export function AgentChart({
13187
})
13288
.catch((err) => {
13389
if (controller.signal.aborted) return;
134-
console.error("Dashboard agent chart request failed:", err);
135-
setState({ status: "error", error: CHART_ERROR_MESSAGE });
90+
setState({ status: "error", error: err?.message ?? "The query failed to run." });
13691
});
13792
return () => controller.abort();
13893
}, [block.query, block.period, organizationId, projectId, environmentId]);
@@ -149,16 +104,16 @@ export function AgentChart({
149104
};
150105

151106
return (
152-
<AgentCard>
107+
<div className="overflow-hidden rounded-lg border border-border-bright bg-background-dimmed">
153108
{block.title ? (
154-
<AgentCardHeader className="text-xs font-medium text-text-dimmed">
109+
<div className="border-b border-grid-bright bg-background-bright px-3 py-2 text-xs font-medium text-text-dimmed">
155110
{block.title}
156-
</AgentCardHeader>
111+
</div>
157112
) : null}
158-
<div className={cn(AGENT_CHART_PLOT_CLASS)}>
113+
<div className="h-64 w-full p-2">
159114
{state.status === "loading" ? (
160115
<div className="flex h-full items-center justify-center gap-2 text-xs text-text-dimmed">
161-
<AgentSpinner size={12} />
116+
<Spinner className="size-3" />
162117
Running query…
163118
</div>
164119
) : state.status === "error" ? (
@@ -174,7 +129,6 @@ export function AgentChart({
174129
/>
175130
)}
176131
</div>
177-
<ChartActions actions={block.actions ?? []} onIntent={onIntent} />
178-
</AgentCard>
132+
</div>
179133
);
180134
}

apps/webapp/app/components/dashboard-agent/AgentUpgradeGate.tsx

Lines changed: 0 additions & 58 deletions
This file was deleted.

0 commit comments

Comments
 (0)