Skip to content

Commit 540b3d3

Browse files
committed
Cleanup TaskDetails
1 parent 14f0476 commit 540b3d3

File tree

9 files changed

+265
-424
lines changed

9 files changed

+265
-424
lines changed

src/components/shared/ContextPanel/Blocks/ActionBlock.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export type Action = {
2121
);
2222

2323
// Temporary: ReactNode included for backward compatibility with some existing buttons. In the long-term we should strive for only Action types.
24-
type ActionOrReactNode = Action | ReactNode;
24+
export type ActionOrReactNode = Action | ReactNode;
2525

2626
interface ActionBlockProps {
2727
title?: string;

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

Lines changed: 21 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { useNavigate } from "@tanstack/react-router";
2-
import { CircleFadingArrowUp, CopyIcon } from "lucide-react";
32
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
43

54
import type { TooltipButtonProps } from "@/components/shared/Buttons/TooltipButton";
@@ -122,42 +121,32 @@ const TaskNodeCard = () => {
122121
setIsEditDialogOpen(false);
123122
}, []);
124123

124+
const { onDuplicate, onUpgrade } = callbacks;
125+
125126
const taskConfigMarkup = useMemo(() => {
126127
const actions: Array<TooltipButtonProps> = [];
127128

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

154147
if (isSubgraphNode && taskId && isSubgraphNavigationEnabled) {
155148
actions.push({
156-
children: (
157-
<div className="flex items-center gap-2">
158-
<Icon name="Workflow" size="sm" />
159-
</div>
160-
),
149+
children: <Icon name="Workflow" size="sm" />,
161150
variant: "outline",
162151
tooltip: `Enter Subgraph: ${subgraphDescription}`,
163152
onClick: () => navigateToSubgraph(taskId),
@@ -166,11 +155,7 @@ const TaskNodeCard = () => {
166155

167156
if (isInAppEditorEnabled) {
168157
actions.push({
169-
children: (
170-
<div className="flex items-center gap-2">
171-
<Icon name="FilePenLine" size="sm" />
172-
</div>
173-
),
158+
children: <Icon name="FilePenLine" size="sm" />,
174159
variant: "outline",
175160
tooltip: "Edit Component Definition",
176161
onClick: handleEditComponent,
@@ -182,15 +167,15 @@ const TaskNodeCard = () => {
182167
taskNode,
183168
nodeId,
184169
readOnly,
185-
callbacks.onDuplicate,
186-
callbacks.onUpgrade,
187170
isInAppEditorEnabled,
188171
isCustomComponent,
189172
isSubgraphNode,
190173
taskId,
191174
subgraphDescription,
192175
navigateToSubgraph,
193176
handleEditComponent,
177+
onDuplicate,
178+
onUpgrade,
194179
]);
195180

196181
const handleInputSectionClick = useCallback(() => {

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,6 @@ const TaskOverview = ({ taskNode, actions }: TaskOverviewProps) => {
129129
url={taskSpec.componentRef.url}
130130
onDelete={callbacks.onDelete}
131131
status={status}
132-
hasDeletionConfirmation={false}
133132
readOnly={readOnly}
134133
actions={detailActions}
135134
/>
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import { type ReactNode } from "react";
2+
import { FaPython } from "react-icons/fa";
3+
4+
import useToastNotification from "@/hooks/useToastNotification";
5+
import type { ComponentSpec } from "@/utils/componentSpec";
6+
import {
7+
downloadStringAsFile,
8+
downloadYamlFromComponentText,
9+
} from "@/utils/URL";
10+
import { componentSpecToText } from "@/utils/yaml";
11+
12+
import {
13+
ActionBlock,
14+
type ActionOrReactNode,
15+
} from "../ContextPanel/Blocks/ActionBlock";
16+
17+
interface TaskActionsProps {
18+
displayName: string;
19+
componentSpec: ComponentSpec;
20+
actions?: ReactNode[];
21+
onDelete?: () => void;
22+
readOnly?: boolean;
23+
className?: string;
24+
}
25+
26+
const TaskActions = ({
27+
displayName,
28+
componentSpec,
29+
actions = [],
30+
onDelete,
31+
readOnly = false,
32+
className,
33+
}: TaskActionsProps) => {
34+
const notify = useToastNotification();
35+
36+
const pythonOriginalCode =
37+
componentSpec?.metadata?.annotations?.original_python_code;
38+
39+
const stringToPythonCodeDownload = () => {
40+
if (!pythonOriginalCode) return;
41+
42+
downloadStringAsFile(
43+
pythonOriginalCode,
44+
`${componentSpec?.name || displayName}.py`,
45+
"text/x-python",
46+
);
47+
};
48+
49+
const handleDownloadYaml = () => {
50+
downloadYamlFromComponentText(componentSpec, displayName);
51+
};
52+
53+
const handleCopyYaml = () => {
54+
const code = componentSpecToText(componentSpec);
55+
56+
navigator.clipboard.writeText(code).then(
57+
() => notify("YAML copied to clipboard", "success"),
58+
(err) => notify("Failed to copy YAML: " + err, "error"),
59+
);
60+
};
61+
62+
const handleDelete = () => {
63+
try {
64+
onDelete?.();
65+
} catch (error) {
66+
console.error("Error deleting component:", error);
67+
notify(`Error deleting component`, "error");
68+
}
69+
};
70+
71+
const orderedActions: ActionOrReactNode[] = [
72+
{
73+
label: "Download YAML",
74+
icon: "Download",
75+
onClick: handleDownloadYaml,
76+
},
77+
{
78+
label: "Download Python Code",
79+
content: <FaPython />,
80+
hidden: !pythonOriginalCode,
81+
onClick: stringToPythonCodeDownload,
82+
},
83+
{
84+
label: "Copy YAML",
85+
icon: "Clipboard",
86+
onClick: handleCopyYaml,
87+
},
88+
...actions,
89+
{
90+
label: "Delete Component",
91+
icon: "Trash",
92+
destructive: true,
93+
hidden: !onDelete || readOnly,
94+
onClick: handleDelete,
95+
},
96+
];
97+
98+
return <ActionBlock actions={orderedActions} className={className} />;
99+
};
100+
101+
export default TaskActions;

0 commit comments

Comments
 (0)