Skip to content

Commit 357442f

Browse files
committed
Cleanup TaskDetails
1 parent 10df8cb commit 357442f

File tree

6 files changed

+275
-358
lines changed

6 files changed

+275
-358
lines changed

src/components/shared/Dialogs/ComponentDetailsDialog.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ const ComponentDetailsDialogContent = withSuspenseWrapper(
140140
url={url}
141141
actions={actions}
142142
onDelete={onDelete}
143+
hasTwoStepDeletion
143144
/>
144145
</TabsContent>
145146

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

Lines changed: 17 additions & 34 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";
@@ -126,38 +125,26 @@ const TaskNodeCard = () => {
126125
const actions: Array<TooltipButtonProps> = [];
127126

128127
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-
);
128+
actions.push({
129+
children: <Icon name="Copy" size="sm" />,
130+
variant: "outline",
131+
tooltip: "Duplicate Task",
132+
onClick: callbacks.onDuplicate,
133+
});
134+
}
135+
136+
if (!readOnly && !isCustomComponent) {
137+
actions.push({
138+
children: <Icon name="CircleFadingArrowUp" size="sm" />,
139+
variant: "outline",
140+
tooltip: "Update Task from Source URL",
141+
onClick: callbacks.onUpgrade,
142+
});
152143
}
153144

154145
if (isSubgraphNode && taskId && isSubgraphNavigationEnabled) {
155146
actions.push({
156-
children: (
157-
<div className="flex items-center gap-2">
158-
<Icon name="Workflow" size="sm" />
159-
</div>
160-
),
147+
children: <Icon name="Workflow" size="sm" />,
161148
variant: "outline",
162149
tooltip: `Enter Subgraph: ${subgraphDescription}`,
163150
onClick: () => navigateToSubgraph(taskId),
@@ -166,11 +153,7 @@ const TaskNodeCard = () => {
166153

167154
if (isInAppEditorEnabled) {
168155
actions.push({
169-
children: (
170-
<div className="flex items-center gap-2">
171-
<Icon name="FilePenLine" size="sm" />
172-
</div>
173-
),
156+
children: <Icon name="FilePenLine" size="sm" />,
174157
variant: "outline",
175158
tooltip: "Edit Component Definition",
176159
onClick: handleEditComponent,

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ const TaskOverview = ({ taskNode, actions }: TaskOverviewProps) => {
8585
<ComponentDetailsDialog
8686
displayName={name}
8787
component={taskSpec.componentRef}
88+
onDelete={callbacks.onDelete}
8889
/>
8990
{readOnly && <StatusIcon status={status} tooltip label="task" />}
9091
</InlineStack>
@@ -128,7 +129,6 @@ const TaskOverview = ({ taskNode, actions }: TaskOverviewProps) => {
128129
url={taskSpec.componentRef.url}
129130
onDelete={callbacks.onDelete}
130131
status={status}
131-
hasDeletionConfirmation={false}
132132
readOnly={readOnly}
133133
actions={detailActions}
134134
/>
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import { type ReactNode, useState } from "react";
2+
import { FaPython } from "react-icons/fa";
3+
4+
import { Icon } from "@/components/ui/icon";
5+
import { InlineStack } from "@/components/ui/layout";
6+
import { Paragraph } from "@/components/ui/typography";
7+
import useToastNotification from "@/hooks/useToastNotification";
8+
import type { ComponentSpec } from "@/utils/componentSpec";
9+
import { downloadYamlFromComponentText } from "@/utils/URL";
10+
import copyToYaml 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+
hasTwoStepDeletion?: boolean;
23+
readOnly?: boolean;
24+
className?: string;
25+
}
26+
27+
const TaskActions = ({
28+
displayName,
29+
componentSpec,
30+
actions = [],
31+
onDelete,
32+
hasTwoStepDeletion = false,
33+
readOnly = false,
34+
className,
35+
}: TaskActionsProps) => {
36+
const notify = useToastNotification();
37+
const [confirmDelete, setConfirmDelete] = useState(false);
38+
39+
const pythonOriginalCode =
40+
componentSpec?.metadata?.annotations?.original_python_code;
41+
42+
const stringToPythonCodeDownload = () => {
43+
if (!pythonOriginalCode) return;
44+
45+
const blob = new Blob([pythonOriginalCode], { type: "text/x-python" });
46+
const url = URL.createObjectURL(blob);
47+
const a = document.createElement("a");
48+
a.href = url;
49+
a.download = `${componentSpec?.name || displayName}.py`;
50+
document.body.appendChild(a);
51+
a.click();
52+
document.body.removeChild(a);
53+
URL.revokeObjectURL(url);
54+
};
55+
56+
const handleDownloadYaml = () => {
57+
downloadYamlFromComponentText(componentSpec, displayName);
58+
};
59+
60+
const handleCopyYaml = () => {
61+
copyToYaml(
62+
componentSpec,
63+
(message) => notify(message, "success"),
64+
(message) => notify(message, "error"),
65+
);
66+
};
67+
68+
const handleDelete = () => {
69+
if (confirmDelete || !hasTwoStepDeletion) {
70+
try {
71+
onDelete?.();
72+
} catch (error) {
73+
console.error("Error deleting component:", error);
74+
notify(`Error deleting component`, "error");
75+
}
76+
} else if (hasTwoStepDeletion) {
77+
setConfirmDelete(true);
78+
}
79+
};
80+
81+
const orderedActions: ActionOrReactNode[] = [];
82+
83+
orderedActions.push({
84+
label: "Download YAML",
85+
icon: "Download",
86+
onClick: handleDownloadYaml,
87+
});
88+
89+
orderedActions.push({
90+
label: "Download Python Code",
91+
content: <FaPython />,
92+
hidden: !pythonOriginalCode,
93+
onClick: stringToPythonCodeDownload,
94+
});
95+
96+
orderedActions.push({
97+
label: "Copy YAML",
98+
icon: "Clipboard",
99+
onClick: handleCopyYaml,
100+
});
101+
102+
orderedActions.push(...actions);
103+
104+
orderedActions.push({
105+
label:
106+
confirmDelete && hasTwoStepDeletion
107+
? "Confirm Delete. This action cannot be undone."
108+
: "Delete Component",
109+
content: (
110+
<InlineStack gap="2" blockAlign="center">
111+
<Icon name="Trash" />
112+
{confirmDelete && hasTwoStepDeletion && (
113+
<Paragraph size="xs" className="text-white">
114+
Confirm Delete
115+
</Paragraph>
116+
)}
117+
</InlineStack>
118+
),
119+
destructive: true,
120+
hidden: !onDelete || readOnly,
121+
onClick: handleDelete,
122+
});
123+
124+
return <ActionBlock actions={orderedActions} className={className} />;
125+
};
126+
127+
export default TaskActions;

0 commit comments

Comments
 (0)