Build a banner for the workflows page (#1591)
This commit is contained in:
@@ -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>
|
||||
|
||||
102
skyvern-frontend/src/routes/workflows/WorkflowsPageBanner.tsx
Normal file
102
skyvern-frontend/src/routes/workflows/WorkflowsPageBanner.tsx
Normal 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 };
|
||||
Reference in New Issue
Block a user