Skip to content

Commit dfc8418

Browse files
authored
feat(task-detail): show custom base image badge on cloud tasks (#3492)
1 parent 0b5987e commit dfc8418

2 files changed

Lines changed: 82 additions & 3 deletions

File tree

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { Cube } from "@phosphor-icons/react";
2+
import type { Task } from "@posthog/shared/domain-types";
3+
import { Badge } from "../../../primitives/Badge";
4+
import { Tooltip } from "../../../primitives/Tooltip";
5+
import { openSettings } from "../../settings/hooks/useOpenSettings";
6+
import { useSandboxCustomImages } from "../../settings/sections/environments/useSandboxCustomImages";
7+
import { useSandboxEnvironments } from "../../settings/sections/environments/useSandboxEnvironments";
8+
9+
export function CustomImageBadge({ task }: { task: Task }) {
10+
const run = task.latest_run;
11+
const state = run?.state as
12+
| { custom_image_id?: unknown; sandbox_environment_id?: unknown }
13+
| undefined;
14+
const customImageId =
15+
typeof state?.custom_image_id === "string" ? state.custom_image_id : null;
16+
const sandboxEnvironmentId =
17+
typeof state?.sandbox_environment_id === "string"
18+
? state.sandbox_environment_id
19+
: null;
20+
21+
// Only mount the data-fetching part when the run could have used a custom image.
22+
if (
23+
run?.environment !== "cloud" ||
24+
(!customImageId && !sandboxEnvironmentId)
25+
) {
26+
return null;
27+
}
28+
return (
29+
<ResolvedCustomImageBadge
30+
customImageId={customImageId}
31+
sandboxEnvironmentId={sandboxEnvironmentId}
32+
/>
33+
);
34+
}
35+
36+
function ResolvedCustomImageBadge({
37+
customImageId,
38+
sandboxEnvironmentId,
39+
}: {
40+
customImageId: string | null;
41+
sandboxEnvironmentId: string | null;
42+
}) {
43+
const { images } = useSandboxCustomImages();
44+
const { environments } = useSandboxEnvironments();
45+
46+
const environment = sandboxEnvironmentId
47+
? environments.find((env) => env.id === sandboxEnvironmentId)
48+
: undefined;
49+
const imageId = customImageId ?? environment?.custom_image_id ?? null;
50+
if (!imageId) return null;
51+
52+
const imageName =
53+
images.find((image) => image.id === imageId)?.name ??
54+
environment?.custom_image_name ??
55+
null;
56+
57+
return (
58+
<Tooltip
59+
content={`Runs on custom base image${imageName ? ` "${imageName}"` : ""}. Click to manage custom images.`}
60+
side="bottom"
61+
delayDuration={300}
62+
>
63+
<button
64+
type="button"
65+
className="no-drag flex shrink-0 cursor-pointer items-center"
66+
aria-label="Manage custom images"
67+
onClick={() => openSettings("cloud-environments")}
68+
>
69+
<Badge color="violet" className="flex items-center gap-1">
70+
<Cube size={10} weight="fill" />
71+
{imageName ? `Custom VM · ${imageName}` : "Custom VM"}
72+
</Badge>
73+
</button>
74+
</Tooltip>
75+
);
76+
}

packages/ui/src/features/task-detail/components/TaskDetail.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { useWorkspace } from "../../workspace/useWorkspace";
2525
import { useWorkspaceEvents } from "../../workspace/useWorkspaceEvents";
2626
import { HeaderTitleEditor } from "../HeaderTitleEditor";
2727
import { useTaskData } from "../hooks/useTaskData";
28+
import { CustomImageBadge } from "./CustomImageBadge";
2829
import { ExternalAppsOpener } from "./ExternalAppsOpener";
2930
import { WorkspaceModeBadge } from "./WorkspaceModeBadge";
3031

@@ -147,12 +148,13 @@ export function TaskDetail({
147148
channelName={channelName}
148149
channelId={channelId}
149150
leafIcon={
150-
workspaceMode ? (
151+
<span className="flex items-center gap-1.5">
151152
<WorkspaceModeBadge
152153
mode={workspaceMode}
153154
checkoutPath={effectiveRepoPath}
154155
/>
155-
) : undefined
156+
<CustomImageBadge task={task} />
157+
</span>
156158
}
157159
leafLabel={task.title}
158160
onRename={handleTitleEditSubmit}
@@ -172,6 +174,7 @@ export function TaskDetail({
172174
mode={workspaceMode}
173175
checkoutPath={effectiveRepoPath}
174176
/>
177+
<CustomImageBadge task={task} />
175178
<Tooltip content={task.title} side="bottom" delayDuration={300}>
176179
<Text
177180
truncate
@@ -189,7 +192,7 @@ export function TaskDetail({
189192
[
190193
channelName,
191194
channelId,
192-
task.title,
195+
task,
193196
trailing,
194197
isEditingTitle,
195198
workspaceMode,

0 commit comments

Comments
 (0)