Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export type Action = {
);

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

interface ActionBlockProps {
title?: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { useNavigate } from "@tanstack/react-router";
import { CircleFadingArrowUp, CopyIcon } from "lucide-react";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";

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

const { onDuplicate, onUpgrade } = callbacks;

const taskConfigMarkup = useMemo(() => {
const actions: Array<TooltipButtonProps> = [];

if (!readOnly) {
actions.push(
{
children: (
<div className="flex items-center gap-2">
<CopyIcon />
</div>
),
variant: "outline",
tooltip: "Duplicate Task",
onClick: callbacks.onDuplicate,
},
{
children: (
<div className="flex items-center gap-2">
<CircleFadingArrowUp />
</div>
),
variant: "outline",
className: cn(isCustomComponent && "hidden"),
tooltip: "Update Task from Source URL",
onClick: callbacks.onUpgrade,
},
);
actions.push({
children: <Icon name="Copy" size="sm" />,
variant: "outline",
tooltip: "Duplicate Task",
onClick: onDuplicate,
});
}

if (!readOnly && !isCustomComponent) {
actions.push({
children: <Icon name="CircleFadingArrowUp" size="sm" />,
variant: "outline",
tooltip: "Update Task from Source URL",
onClick: onUpgrade,
});
}

if (isSubgraphNode && taskId && isSubgraphNavigationEnabled) {
actions.push({
children: (
<div className="flex items-center gap-2">
<Icon name="Workflow" size="sm" />
</div>
),
children: <Icon name="Workflow" size="sm" />,
variant: "outline",
tooltip: `Enter Subgraph: ${subgraphDescription}`,
onClick: () => navigateToSubgraph(taskId),
Expand All @@ -166,11 +155,7 @@ const TaskNodeCard = () => {

if (isInAppEditorEnabled) {
actions.push({
children: (
<div className="flex items-center gap-2">
<Icon name="FilePenLine" size="sm" />
</div>
),
children: <Icon name="FilePenLine" size="sm" />,
variant: "outline",
tooltip: "Edit Component Definition",
onClick: handleEditComponent,
Expand All @@ -182,15 +167,15 @@ const TaskNodeCard = () => {
taskNode,
nodeId,
readOnly,
callbacks.onDuplicate,
callbacks.onUpgrade,
isInAppEditorEnabled,
isCustomComponent,
isSubgraphNode,
taskId,
subgraphDescription,
navigateToSubgraph,
handleEditComponent,
onDuplicate,
onUpgrade,
]);

const handleInputSectionClick = useCallback(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,6 @@ const TaskOverview = ({ taskNode, actions }: TaskOverviewProps) => {
url={taskSpec.componentRef.url}
onDelete={callbacks.onDelete}
status={status}
hasDeletionConfirmation={false}
readOnly={readOnly}
actions={detailActions}
/>
Expand Down
101 changes: 101 additions & 0 deletions src/components/shared/TaskDetails/Actions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { type ReactNode } from "react";
import { FaPython } from "react-icons/fa";

import useToastNotification from "@/hooks/useToastNotification";
import type { ComponentSpec } from "@/utils/componentSpec";
import {
downloadStringAsFile,
downloadYamlFromComponentText,
} from "@/utils/URL";
import { componentSpecToText } from "@/utils/yaml";

import {
ActionBlock,
type ActionOrReactNode,
} from "../ContextPanel/Blocks/ActionBlock";

interface TaskActionsProps {
displayName: string;
componentSpec: ComponentSpec;
actions?: ReactNode[];
onDelete?: () => void;
readOnly?: boolean;
className?: string;
}

const TaskActions = ({
displayName,
componentSpec,
actions = [],
onDelete,
readOnly = false,
className,
}: TaskActionsProps) => {
const notify = useToastNotification();

const pythonOriginalCode =
componentSpec?.metadata?.annotations?.original_python_code;

const stringToPythonCodeDownload = () => {
if (!pythonOriginalCode) return;

downloadStringAsFile(
pythonOriginalCode,
`${componentSpec?.name || displayName}.py`,
"text/x-python",
);
};

const handleDownloadYaml = () => {
downloadYamlFromComponentText(componentSpec, displayName);
};

const handleCopyYaml = () => {
const code = componentSpecToText(componentSpec);

navigator.clipboard.writeText(code).then(
() => notify("YAML copied to clipboard", "success"),
(err) => notify("Failed to copy YAML: " + err, "error"),
);
};

const handleDelete = () => {
try {
onDelete?.();
} catch (error) {
console.error("Error deleting component:", error);
notify(`Error deleting component`, "error");
}
};

const orderedActions: ActionOrReactNode[] = [
{
label: "Download YAML",
icon: "Download",
onClick: handleDownloadYaml,
},
{
label: "Download Python Code",
content: <FaPython />,
hidden: !pythonOriginalCode,
onClick: stringToPythonCodeDownload,
},
{
label: "Copy YAML",
icon: "Clipboard",
onClick: handleCopyYaml,
},
...actions,
{
label: "Delete Component",
icon: "Trash",
destructive: true,
hidden: !onDelete || readOnly,
onClick: handleDelete,
},
];

return <ActionBlock actions={orderedActions} className={className} />;
};

export default TaskActions;
Loading
Loading