Build a banner for the workflows page (#1591)

This commit is contained in:
Shuchang Zheng
2025-01-17 10:49:50 -08:00
committed by GitHub
parent e4cb0987cb
commit 4b7ed84cd7
2 changed files with 111 additions and 93 deletions

View File

@@ -1,6 +1,6 @@
import { getClient } from "@/api/AxiosClient";
import { 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,
@@ -25,40 +25,20 @@ import {
TooltipTrigger,
} from "@/components/ui/tooltip";
import { useCredentialGetter } from "@/hooks/useCredentialGetter";
import { downloadBlob } from "@/util/downloadBlob";
import { basicLocalTimeFormat, basicTimeFormat } from "@/util/timeFormat";
import { cn } from "@/util/utils";
import {
DownloadIcon,
ExclamationTriangleIcon,
Pencil2Icon,
PlayIcon,
PlusIcon,
ReloadIcon,
} from "@radix-ui/react-icons";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { DownloadIcon, Pencil2Icon, PlayIcon } from "@radix-ui/react-icons";
import { useQuery } 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 { WorkflowActions } from "./WorkflowActions";
import { WorkflowTitle } from "./WorkflowTitle";
import { WorkflowApiResponse } from "./types/workflowTypes";
import { WorkflowRunApiResponse } from "@/api/types";
import { downloadBlob } from "@/util/downloadBlob";
const emptyWorkflowRequest: WorkflowCreateYAMLRequest = {
title: "New Workflow",
description: "",
workflow_definition: {
blocks: [],
parameters: [],
},
};
import { WorkflowActions } from "./WorkflowActions";
import { WorkflowsPageBanner } from "./WorkflowsPageBanner";
import { WorkflowTitle } from "./WorkflowTitle";
function Workflows() {
const credentialGetter = useCredentialGetter();
const navigate = useNavigate();
const queryClient = useQueryClient();
const [searchParams, setSearchParams] = useSearchParams();
const workflowsPage = searchParams.get("workflowsPage")
? Number(searchParams.get("workflowsPage"))
@@ -99,27 +79,6 @@ function Workflows() {
refetchOnMount: "always",
});
const createNewWorkflowMutation = useMutation({
mutationFn: async () => {
const client = await getClient(credentialGetter);
const yaml = convertToYAML(emptyWorkflowRequest);
return client.post<
typeof emptyWorkflowRequest,
{ data: WorkflowApiResponse }
>("/workflows", yaml, {
headers: {
"Content-Type": "text/plain",
},
});
},
onSuccess: (response) => {
queryClient.invalidateQueries({
queryKey: ["workflows"],
});
navigate(`/workflows/${response.data.workflow_permanent_id}/edit`);
},
});
function handleExport() {
if (!workflowRuns) {
return; // should never happen
@@ -176,55 +135,12 @@ function Workflows() {
navigate(path);
}
const showExperimentalMessage =
workflows?.length === 0 && workflowsPage === 1;
return (
<div className="space-y-8">
{showExperimentalMessage && (
<Alert variant="default" className="bg-slate-elevation2">
<AlertTitle>
<div className="flex items-center gap-2">
<ExclamationTriangleIcon className="h-6 w-6" />
<span className="text-xl">Experimental Feature</span>
</div>
</AlertTitle>
<AlertDescription className="text-base text-slate-300">
Workflows are still in experimental mode. Please{" "}
{
<a
href="https://meetings.hubspot.com/skyvern/demo"
target="_blank"
rel="noopener noreferrer"
className="ml-auto underline underline-offset-2"
>
book a demo
</a>
}{" "}
if you'd like to learn more. If you're feeling adventurous, create
your first workflow!
</AlertDescription>
</Alert>
)}
<WorkflowsPageBanner />
<div className="space-y-4">
<header className="flex items-center justify-between">
<header>
<h1 className="text-2xl font-semibold">Workflows</h1>
<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>

View File

@@ -0,0 +1,102 @@
import { Button } from "@/components/ui/button";
import { ImportWorkflowButton } from "./ImportWorkflowButton";
import { useNavigate } from "react-router-dom";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { getClient } from "@/api/AxiosClient";
import { useCredentialGetter } from "@/hooks/useCredentialGetter";
import { WorkflowCreateYAMLRequest } from "./types/workflowYamlTypes";
import { stringify as convertToYAML } from "yaml";
import { WorkflowApiResponse } from "./types/workflowTypes";
import { PlusIcon, ReloadIcon } from "@radix-ui/react-icons";
const emptyWorkflowRequest: WorkflowCreateYAMLRequest = {
title: "New Workflow",
description: "",
workflow_definition: {
blocks: [],
parameters: [],
},
};
function WorkflowsPageBanner() {
const navigate = useNavigate();
const credentialGetter = useCredentialGetter();
const queryClient = useQueryClient();
const createNewWorkflowMutation = useMutation({
mutationFn: async () => {
const client = await getClient(credentialGetter);
const yaml = convertToYAML(emptyWorkflowRequest);
return client.post<
typeof emptyWorkflowRequest,
{ data: WorkflowApiResponse }
>("/workflows", yaml, {
headers: {
"Content-Type": "text/plain",
},
});
},
onSuccess: (response) => {
queryClient.invalidateQueries({
queryKey: ["workflows"],
});
navigate(`/workflows/${response.data.workflow_permanent_id}/edit`);
},
});
return (
<div className="space-y-8 bg-slate-elevation1 p-12">
<div className="flex justify-center text-3xl font-bold">
<h1>Workflows</h1>
</div>
<div className="flex justify-center gap-4">
<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>
<div className="flex">
<div className="mx-auto flex flex-col gap-3">
<div className="font-bold">
Workflows let you create complex web-agents that can:
</div>
<div className="flex gap-2">
<div className="flex size-6 items-center justify-center rounded-full bg-primary text-primary-foreground">
1
</div>
<div>Save browser sessions and re-use them in subsequent runs</div>
</div>
<div className="flex gap-2">
<div className="flex size-6 items-center justify-center rounded-full bg-primary text-primary-foreground">
2
</div>
<div>
Connect multiple agents together to carry out complex objectives
</div>
</div>
<div className="flex gap-2">
<div className="flex size-6 items-center justify-center rounded-full bg-primary text-primary-foreground">
3
</div>
<div>
Allow Skyvern agents to execute non-browser tasks such as sending
emails, or parsing PDFs
</div>
</div>
</div>
</div>
</div>
);
}
export { WorkflowsPageBanner };