Skip to content

Commit f1fefa5

Browse files
committed
Cleanup TaskDetails
1 parent 93c5ed4 commit f1fefa5

File tree

8 files changed

+515
-488
lines changed

8 files changed

+515
-488
lines changed

src/components/shared/ExecutionDetails/ExecutionDetails.tsx

Lines changed: 103 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,7 @@
1-
import { ChevronsUpDown, ExternalLink } from "lucide-react";
2-
31
import type { GetContainerExecutionStateResponse } from "@/api/types.gen";
4-
import { Button } from "@/components/ui/button";
5-
import {
6-
Collapsible,
7-
CollapsibleContent,
8-
CollapsibleTrigger,
9-
} from "@/components/ui/collapsible";
2+
import { BlockStack, InlineStack } from "@/components/ui/layout";
103
import { Skeleton } from "@/components/ui/skeleton";
4+
import { Paragraph } from "@/components/ui/typography";
115
import { useBackend } from "@/providers/BackendProvider";
126
import { useFetchContainerExecutionState } from "@/services/executionService";
137
import {
@@ -18,6 +12,8 @@ import { EXIT_CODE_OOM } from "@/utils/constants";
1812
import { formatDate, formatDuration } from "@/utils/date";
1913

2014
import { InfoBox } from "../InfoBox";
15+
import { ContentBlock } from "../TaskDetails/Blocks/ContentBlock";
16+
import { URLBlock } from "../TaskDetails/Blocks/LinkBlock";
2117

2218
interface ExecutionDetailsProps {
2319
executionId: string;
@@ -37,156 +33,126 @@ export const ExecutionDetails = ({
3733
data: containerState,
3834
isLoading: isLoadingContainerState,
3935
error: containerStateError,
40-
} = useFetchContainerExecutionState(executionId || "", backendUrl);
41-
42-
// Don't render if no execution data is available
43-
const hasExecutionData =
44-
executionId ||
45-
containerState ||
46-
isLoadingContainerState ||
47-
containerStateError;
48-
if (!hasExecutionData) {
49-
return null;
50-
}
36+
} = useFetchContainerExecutionState(executionId, backendUrl);
37+
38+
const loadingMarkup = (
39+
<BlockStack gap="2">
40+
<InlineStack gap="2" blockAlign="center">
41+
<Skeleton className="h-3 w-12" />
42+
<Skeleton className="h-3 w-32" />
43+
</InlineStack>
44+
<InlineStack gap="2" blockAlign="center">
45+
<Skeleton className="h-3 w-20" />
46+
<Skeleton className="h-3 w-24" />
47+
<Skeleton className="h-3 w-40" />
48+
</InlineStack>
49+
</BlockStack>
50+
);
5151

5252
const podName = executionPodName(containerState);
5353
const executionJobLinks = getExecutionJobLinks(containerState);
5454

5555
return (
56-
<div className="flex flex-col px-3 py-2">
57-
<Collapsible defaultOpen>
58-
<div className="font-medium text-sm text-foreground flex items-center gap-1">
59-
Execution Details
60-
<CollapsibleTrigger asChild>
61-
<Button variant="ghost" size="sm">
62-
<ChevronsUpDown className="h-4 w-4" />
63-
<span className="sr-only">Toggle</span>
64-
</Button>
65-
</CollapsibleTrigger>
66-
</div>
67-
68-
<CollapsibleContent className="mt-2 space-y-2">
69-
<div className="flex items-center gap-2">
70-
<span className="font-medium text-xs text-foreground min-w-fit">
71-
Execution ID:
72-
</span>
73-
<span className="text-xs text-muted-foreground font-mono truncate">
74-
{executionId}
75-
</span>
76-
</div>
56+
<ContentBlock
57+
title="Execution Details"
58+
collapsible
59+
defaultOpen
60+
content={
61+
<BlockStack gap="2">
62+
<ExecutionItem name="Execution ID" value={executionId} />
7763

7864
{!isSubgraph && (
7965
<>
80-
{isLoadingContainerState && (
81-
<div className="space-y-2">
82-
<div className="flex items-center gap-2">
83-
<Skeleton className="h-3 w-12" />
84-
<Skeleton className="h-3 w-32" />
85-
</div>
86-
<div className="flex items-center gap-2">
87-
<Skeleton className="h-3 w-20" />
88-
<Skeleton className="h-3 w-24" />
89-
<Skeleton className="h-3 w-40" />
90-
</div>
91-
</div>
92-
)}
93-
94-
{containerStateError && (
95-
<InfoBox title="Failed to load container state" variant="error">
96-
{containerStateError.message}
97-
</InfoBox>
98-
)}
66+
{isLoadingContainerState && loadingMarkup}
9967

10068
{!!containerState?.exit_code && (
101-
<div className="flex flex-wrap items-center gap-1">
102-
<span className="font-medium text-xs text-destructive">
103-
Exit Code:
104-
</span>
105-
<span className="text-xs text-destructive">
106-
{containerState.exit_code}
107-
</span>
108-
{containerState.exit_code === EXIT_CODE_OOM && (
109-
<span className="text-xs text-destructive">
110-
(Out of Memory)
111-
</span>
112-
)}
113-
</div>
69+
<ExecutionItem
70+
name="Exit Code"
71+
critical
72+
value={
73+
containerState.exit_code === EXIT_CODE_OOM
74+
? `${containerState.exit_code} (Out of Memory)`
75+
: `${containerState.exit_code}`
76+
}
77+
/>
11478
)}
11579

11680
{containerState?.started_at && (
117-
<div className="flex items-center gap-2">
118-
<span className="font-medium text-xs text-foreground min-w-fit">
119-
Started:
120-
</span>
121-
<span className="text-xs text-muted-foreground">
122-
{formatDate(containerState.started_at)}
123-
</span>
124-
</div>
81+
<ExecutionItem
82+
name="Started"
83+
value={formatDate(containerState.started_at)}
84+
/>
12585
)}
12686

12787
{containerState?.ended_at && containerState?.started_at && (
128-
<div className="flex items-center gap-2">
129-
<span className="font-medium text-xs text-foreground min-w-fit">
130-
Completed in:
131-
</span>
132-
<span className="text-xs text-muted-foreground">
133-
{formatDuration(
134-
containerState.started_at,
135-
containerState.ended_at,
136-
)}
137-
</span>
138-
<span className="text-xs text-muted-foreground">
139-
({formatDate(containerState.ended_at)})
140-
</span>
141-
</div>
88+
<ExecutionItem
89+
name="Completed in"
90+
value={`${formatDuration(
91+
containerState.started_at,
92+
containerState.ended_at,
93+
)} (${formatDate(containerState.ended_at)})`}
94+
/>
14295
)}
14396

144-
{podName && (
145-
<div className="flex items-center gap-2">
146-
<span className="font-medium text-xs text-foreground min-w-fit">
147-
Pod Name:
148-
</span>
149-
<span className="text-xs text-muted-foreground">
150-
{podName}
151-
</span>
152-
</div>
153-
)}
154-
{executionJobLinks && (
155-
<>
156-
{executionJobLinks.map((linkInfo) => (
157-
<div
158-
key={linkInfo.name}
159-
className="flex text-xs items-center gap-2"
160-
>
161-
<span className="font-medium text-foreground min-w-fit">
162-
{linkInfo.name}:
163-
</span>
164-
<a
165-
href={linkInfo.url}
166-
className="text-sky-500 hover:underline flex items-center gap-1"
167-
target="_blank"
168-
rel="noopener noreferrer"
169-
>
170-
{linkInfo.value}
171-
<ExternalLink className="size-3 shrink-0" />
172-
</a>
173-
</div>
174-
))}
175-
</>
176-
)}
97+
{podName && <ExecutionItem name="Pod Name" value={podName} />}
98+
99+
{executionJobLinks?.map((linkInfo) => (
100+
<ExecutionItem
101+
key={linkInfo.name + linkInfo.value}
102+
name={linkInfo.name}
103+
value={linkInfo}
104+
/>
105+
))}
177106

178-
{!isLoadingContainerState &&
179-
!containerState &&
180-
!containerStateError && (
181-
<div className="text-xs text-muted-foreground">
182-
Container state not available
183-
</div>
184-
)}
107+
{containerStateError && (
108+
<InfoBox
109+
title="Failed to load container state"
110+
variant="error"
111+
className="wrap-anywhere"
112+
>
113+
{containerStateError.message}
114+
</InfoBox>
115+
)}
185116
</>
186117
)}
187-
</CollapsibleContent>
188-
</Collapsible>
189-
</div>
118+
</BlockStack>
119+
}
120+
/>
121+
);
122+
};
123+
124+
const ExecutionItem = ({
125+
name,
126+
value,
127+
critical,
128+
}: {
129+
name: string;
130+
value: string | ExecutionLinkItem;
131+
critical?: boolean;
132+
}) => {
133+
const isLinkItem = (
134+
val: string | ExecutionLinkItem,
135+
): val is ExecutionLinkItem => {
136+
return typeof val === "object" && val !== null && "value" in val;
137+
};
138+
139+
return (
140+
<InlineStack gap="2" blockAlign="center" wrap="nowrap">
141+
<Paragraph size="xs" tone={critical ? "critical" : "inherit"}>
142+
{name}:
143+
</Paragraph>
144+
{isLinkItem(value) ? (
145+
<URLBlock href={value.url} text={value.value} />
146+
) : (
147+
<Paragraph
148+
size="xs"
149+
tone={critical ? "critical" : "subdued"}
150+
className="truncate"
151+
>
152+
{value}
153+
</Paragraph>
154+
)}
155+
</InlineStack>
190156
);
191157
};
192158

src/components/shared/ReactFlow/FlowCanvas/TaskNode/TaskNodeCard/TaskNodeCard.tsx

Lines changed: 17 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { useNavigate } from "@tanstack/react-router";
22
import { useStore } from "@xyflow/react";
3-
import { CircleFadingArrowUp, CopyIcon } from "lucide-react";
43
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
54

65
import type { TooltipButtonProps } from "@/components/shared/Buttons/TooltipButton";
@@ -132,38 +131,26 @@ const TaskNodeCard = () => {
132131
const actions: Array<TooltipButtonProps> = [];
133132

134133
if (!readOnly) {
135-
actions.push(
136-
{
137-
children: (
138-
<div className="flex items-center gap-2">
139-
<CopyIcon />
140-
</div>
141-
),
142-
variant: "outline",
143-
tooltip: "Duplicate Task",
144-
onClick: callbacks.onDuplicate,
145-
},
146-
{
147-
children: (
148-
<div className="flex items-center gap-2">
149-
<CircleFadingArrowUp />
150-
</div>
151-
),
152-
variant: "outline",
153-
className: cn(isCustomComponent && "hidden"),
154-
tooltip: "Update Task from Source URL",
155-
onClick: callbacks.onUpgrade,
156-
},
157-
);
134+
actions.push({
135+
children: <Icon name="Copy" size="sm" />,
136+
variant: "outline",
137+
tooltip: "Duplicate Task",
138+
onClick: callbacks.onDuplicate,
139+
});
140+
}
141+
142+
if (!readOnly && !isCustomComponent) {
143+
actions.push({
144+
children: <Icon name="CircleFadingArrowUp" size="sm" />,
145+
variant: "outline",
146+
tooltip: "Update Task from Source URL",
147+
onClick: callbacks.onUpgrade,
148+
});
158149
}
159150

160151
if (isSubgraphNode && taskId && isSubgraphNavigationEnabled) {
161152
actions.push({
162-
children: (
163-
<div className="flex items-center gap-2">
164-
<Icon name="Workflow" size="sm" />
165-
</div>
166-
),
153+
children: <Icon name="Workflow" size="sm" />,
167154
variant: "outline",
168155
tooltip: `Enter Subgraph: ${subgraphDescription}`,
169156
onClick: () => navigateToSubgraph(taskId),
@@ -172,11 +159,7 @@ const TaskNodeCard = () => {
172159

173160
if (isInAppEditorEnabled) {
174161
actions.push({
175-
children: (
176-
<div className="flex items-center gap-2">
177-
<Icon name="FilePenLine" size="sm" />
178-
</div>
179-
),
162+
children: <Icon name="FilePenLine" size="sm" />,
180163
variant: "outline",
181164
tooltip: "Edit Component Definition",
182165
onClick: handleEditComponent,

0 commit comments

Comments
 (0)