Import workflow (#986)

This commit is contained in:
Shuchang Zheng
2024-10-16 09:04:21 -07:00
committed by GitHub
parent ba6ef89b35
commit fd061d1262
2 changed files with 120 additions and 15 deletions

View File

@@ -0,0 +1,101 @@
import { getClient } from "@/api/AxiosClient";
import { Label } from "@/components/ui/label";
import { ReloadIcon, UploadIcon } from "@radix-ui/react-icons";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { useId } from "react";
import { stringify as convertToYAML } from "yaml";
import { WorkflowApiResponse } from "./types/workflowTypes";
import { useCredentialGetter } from "@/hooks/useCredentialGetter";
import { useNavigate } from "react-router-dom";
import { AxiosError } from "axios";
import { toast } from "@/components/ui/use-toast";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
function isJsonString(str: string): boolean {
try {
JSON.parse(str);
} catch (e) {
return false;
}
return true;
}
function ImportWorkflowButton() {
const inputId = useId();
const credentialGetter = useCredentialGetter();
const queryClient = useQueryClient();
const navigate = useNavigate();
const createWorkflowFromYamlMutation = useMutation({
mutationFn: async (yaml: string) => {
const client = await getClient(credentialGetter);
return client.post<string, { data: WorkflowApiResponse }>(
"/workflows",
yaml,
{
headers: {
"Content-Type": "text/plain",
},
},
);
},
onSuccess: (response) => {
queryClient.invalidateQueries({
queryKey: ["workflows"],
});
navigate(`/workflows/${response.data.workflow_permanent_id}/edit`);
},
onError: (error: AxiosError) => {
toast({
variant: "destructive",
title: "Error importing workflow",
description: error.message || "An error occurred",
});
},
});
return (
<TooltipProvider>
<Tooltip>
<TooltipTrigger>
<Label htmlFor={inputId}>
<input
id={inputId}
type="file"
accept=".yaml,.yml,.json"
className="hidden"
onChange={async (event) => {
if (event.target.files && event.target.files[0]) {
const fileTextContent = await event.target.files[0].text();
const isJson = isJsonString(fileTextContent);
const content = isJson
? convertToYAML(JSON.parse(fileTextContent))
: fileTextContent;
createWorkflowFromYamlMutation.mutate(content);
}
}}
/>
<div className="flex h-full cursor-pointer items-center gap-2 rounded-md bg-secondary px-4 py-2 font-bold text-secondary-foreground hover:bg-secondary/90">
{createWorkflowFromYamlMutation.isPending ? (
<ReloadIcon className="h-4 w-4 animate-spin" />
) : (
<UploadIcon className="h-4 w-4" />
)}
Import
</div>
</Label>
</TooltipTrigger>
<TooltipContent>
Import a workflow from a YAML or JSON file
</TooltipContent>
</Tooltip>
</TooltipProvider>
);
}
export { ImportWorkflowButton };

View File

@@ -1,6 +1,7 @@
import { getClient } from "@/api/AxiosClient";
import { WorkflowApiResponse, WorkflowRunApiResponse } from "@/api/types";
import { StatusBadge } from "@/components/StatusBadge";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Button } from "@/components/ui/button";
import {
Pagination,
@@ -37,10 +38,10 @@ import {
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { useNavigate, useSearchParams } from "react-router-dom";
import { stringify as convertToYAML } from "yaml";
import { ImportWorkflowButton } from "./ImportWorkflowButton";
import { WorkflowCreateYAMLRequest } from "./types/workflowYamlTypes";
import { WorkflowTitle } from "./WorkflowTitle";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { WorkflowActions } from "./WorkflowActions";
import { WorkflowTitle } from "./WorkflowTitle";
const emptyWorkflowRequest: WorkflowCreateYAMLRequest = {
title: "New Workflow",
@@ -178,19 +179,22 @@ function Workflows() {
<header className="flex items-center justify-between">
<h1 className="text-2xl font-semibold">Workflows</h1>
<Button
disabled={createNewWorkflowMutation.isPending}
onClick={() => {
createNewWorkflowMutation.mutate();
}}
>
{createNewWorkflowMutation.isPending ? (
<ReloadIcon className="mr-2 h-4 w-4 animate-spin" />
) : (
<PlusIcon className="mr-2 h-4 w-4" />
)}
Create Workflow
</Button>
<div className="flex gap-2">
<ImportWorkflowButton />
<Button
disabled={createNewWorkflowMutation.isPending}
onClick={() => {
createNewWorkflowMutation.mutate();
}}
>
{createNewWorkflowMutation.isPending ? (
<ReloadIcon className="mr-2 h-4 w-4 animate-spin" />
) : (
<PlusIcon className="mr-2 h-4 w-4" />
)}
Create Workflow
</Button>
</div>
</header>
<div className="rounded-md border">
<Table>