From a22592001b97672ced4072cd1f597091fbec72ea Mon Sep 17 00:00:00 2001 From: Shuchang Zheng Date: Thu, 5 Dec 2024 11:56:09 -0800 Subject: [PATCH] Skyvern Forms UI (#1330) --- skyvern-frontend/src/api/types.ts | 21 +-- .../src/components/NavLinkGroup.tsx | 87 ++++++++++ .../src/components/StatusFilterDropdown.tsx | 57 +++++++ .../src/components/icons/BagIcon.tsx | 26 +++ .../src/components/icons/GovernmentIcon.tsx | 26 +++ .../src/components/icons/ReceiptIcon.tsx | 26 +++ .../src/components/icons/ToolIcon.tsx | 26 +++ skyvern-frontend/src/router.tsx | 40 ++--- skyvern-frontend/src/routes/root/SideNav.tsx | 104 ++++-------- .../src/routes/root/SidebarContent.tsx | 2 +- .../routes/tasks/create/CreateNewTaskForm.tsx | 3 +- .../src/routes/tasks/create/PromptBox.tsx | 20 ++- .../routes/tasks/create/retry/RetryTask.tsx | 12 +- .../src/routes/tasks/detail/TaskDetails.tsx | 2 +- .../routes/tasks/detail/TaskParameters.tsx | 4 +- .../src/routes/tasks/list/TaskHistory.tsx | 155 ++++++++++++------ .../src/routes/tasks/list/TaskList.tsx | 59 ------- .../src/routes/tasks/list/TasksPage.tsx | 43 +++++ 18 files changed, 479 insertions(+), 234 deletions(-) create mode 100644 skyvern-frontend/src/components/NavLinkGroup.tsx create mode 100644 skyvern-frontend/src/components/StatusFilterDropdown.tsx create mode 100644 skyvern-frontend/src/components/icons/BagIcon.tsx create mode 100644 skyvern-frontend/src/components/icons/GovernmentIcon.tsx create mode 100644 skyvern-frontend/src/components/icons/ReceiptIcon.tsx create mode 100644 skyvern-frontend/src/components/icons/ToolIcon.tsx delete mode 100644 skyvern-frontend/src/routes/tasks/list/TaskList.tsx create mode 100644 skyvern-frontend/src/routes/tasks/list/TasksPage.tsx diff --git a/skyvern-frontend/src/api/types.ts b/skyvern-frontend/src/api/types.ts index 526995b3..c6ecedf1 100644 --- a/skyvern-frontend/src/api/types.ts +++ b/skyvern-frontend/src/api/types.ts @@ -93,17 +93,18 @@ export type TaskApiResponse = { }; export type CreateTaskRequest = { - title: string | null; + title?: string | null; url: string; - webhook_callback_url: string | null; - navigation_goal: string | null; - data_extraction_goal: string | null; - navigation_payload: Record | string | null; - extracted_information_schema: Record | string | null; - error_code_mapping: Record | null; - proxy_location: ProxyLocation | null; - totp_verification_url: string | null; - totp_identifier: string | null; + webhook_callback_url?: string | null; + navigation_goal?: string | null; + data_extraction_goal?: string | null; + navigation_payload?: Record | string | null; + extracted_information_schema?: Record | string | null; + error_code_mapping?: Record | null; + proxy_location?: ProxyLocation | null; + totp_verification_url?: string | null; + totp_identifier?: string | null; + application?: string | null; }; export type User = { diff --git a/skyvern-frontend/src/components/NavLinkGroup.tsx b/skyvern-frontend/src/components/NavLinkGroup.tsx new file mode 100644 index 00000000..f65401b6 --- /dev/null +++ b/skyvern-frontend/src/components/NavLinkGroup.tsx @@ -0,0 +1,87 @@ +import { useSidebarStore } from "@/store/SidebarStore"; +import { cn } from "@/util/utils"; +import { NavLink, useMatches } from "react-router-dom"; +import { Badge } from "./ui/badge"; + +type Props = { + title: string; + links: Array<{ + label: string; + to: string; + disabled?: boolean; + icon?: React.ReactNode; + }>; +}; + +function NavLinkGroup({ title, links }: Props) { + const { collapsed } = useSidebarStore(); + const matches = useMatches(); + const groupIsActive = matches.some((match) => { + const inputs = links.map((link) => link.to); + return inputs.includes(match.pathname); + }); + + return ( +
+
+
+ {title} +
+
+
+ {links.map((link) => { + return ( + { + return cn( + "block rounded-lg py-2 pl-3 text-slate-400 hover:bg-muted hover:text-primary", + { + "bg-muted": isActive, + }, + { + "text-primary": groupIsActive, + "px-3": collapsed, + }, + ); + }} + > +
+
+ {link.icon} + {!collapsed && link.label} +
+ {!collapsed && link.disabled && ( + + Training + + )} +
+
+ ); + })} +
+
+ ); +} + +export { NavLinkGroup }; diff --git a/skyvern-frontend/src/components/StatusFilterDropdown.tsx b/skyvern-frontend/src/components/StatusFilterDropdown.tsx new file mode 100644 index 00000000..86a1649e --- /dev/null +++ b/skyvern-frontend/src/components/StatusFilterDropdown.tsx @@ -0,0 +1,57 @@ +import { ChevronDownIcon } from "@radix-ui/react-icons"; +import { Button } from "./ui/button"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuTrigger, +} from "./ui/dropdown-menu"; +import { Checkbox } from "./ui/checkbox"; +import { Status } from "@/api/types"; + +type Item = { + label: string; + value: Status; +}; + +type Props = { + options: Array; + values: Array; + onChange: (values: Array) => void; +}; + +function StatusFilterDropdown({ options, values, onChange }: Props) { + return ( + + + + + + {options.map((item) => { + return ( +
+ { + if (checked) { + onChange([...values, item.value]); + } else { + onChange(values.filter((value) => value !== item.value)); + } + }} + /> + +
+ ); + })} +
+
+ ); +} + +export { StatusFilterDropdown }; diff --git a/skyvern-frontend/src/components/icons/BagIcon.tsx b/skyvern-frontend/src/components/icons/BagIcon.tsx new file mode 100644 index 00000000..a519dcb6 --- /dev/null +++ b/skyvern-frontend/src/components/icons/BagIcon.tsx @@ -0,0 +1,26 @@ +type Props = { + className?: string; +}; + +function BagIcon({ className }: Props) { + return ( + + + + ); +} + +export { BagIcon }; diff --git a/skyvern-frontend/src/components/icons/GovernmentIcon.tsx b/skyvern-frontend/src/components/icons/GovernmentIcon.tsx new file mode 100644 index 00000000..9cf0517f --- /dev/null +++ b/skyvern-frontend/src/components/icons/GovernmentIcon.tsx @@ -0,0 +1,26 @@ +type Props = { + className?: string; +}; + +function GovernmentIcon({ className }: Props) { + return ( + + + + ); +} + +export { GovernmentIcon }; diff --git a/skyvern-frontend/src/components/icons/ReceiptIcon.tsx b/skyvern-frontend/src/components/icons/ReceiptIcon.tsx new file mode 100644 index 00000000..ba08d1c7 --- /dev/null +++ b/skyvern-frontend/src/components/icons/ReceiptIcon.tsx @@ -0,0 +1,26 @@ +type Props = { + className?: string; +}; + +function ReceiptIcon({ className }: Props) { + return ( + + + + ); +} + +export { ReceiptIcon }; diff --git a/skyvern-frontend/src/components/icons/ToolIcon.tsx b/skyvern-frontend/src/components/icons/ToolIcon.tsx new file mode 100644 index 00000000..5f6b5a01 --- /dev/null +++ b/skyvern-frontend/src/components/icons/ToolIcon.tsx @@ -0,0 +1,26 @@ +type Props = { + className?: string; +}; + +function ToolIcon({ className }: Props) { + return ( + + + + ); +} + +export { ToolIcon }; diff --git a/skyvern-frontend/src/router.tsx b/skyvern-frontend/src/router.tsx index 7e03ce3b..b426edbf 100644 --- a/skyvern-frontend/src/router.tsx +++ b/skyvern-frontend/src/router.tsx @@ -4,15 +4,13 @@ import { Settings } from "./routes/settings/Settings"; import { SettingsPageLayout } from "./routes/settings/SettingsPageLayout"; import { TasksPageLayout } from "./routes/tasks/TasksPageLayout"; import { CreateNewTaskFormPage } from "./routes/tasks/create/CreateNewTaskFormPage"; -import { CreateNewTaskLayout } from "./routes/tasks/create/CreateNewTaskLayout"; -import { TaskTemplates } from "./routes/tasks/create/TaskTemplates"; import { RetryTask } from "./routes/tasks/create/retry/RetryTask"; import { StepArtifactsLayout } from "./routes/tasks/detail/StepArtifactsLayout"; import { TaskActions } from "./routes/tasks/detail/TaskActions"; import { TaskDetails } from "./routes/tasks/detail/TaskDetails"; import { TaskParameters } from "./routes/tasks/detail/TaskParameters"; import { TaskRecording } from "./routes/tasks/detail/TaskRecording"; -import { TaskList } from "./routes/tasks/list/TaskList"; +import { TasksPage } from "./routes/tasks/list/TasksPage"; import { WorkflowPage } from "./routes/workflows/WorkflowPage"; import { WorkflowRun } from "./routes/workflows/WorkflowRun"; import { WorkflowRunParameters } from "./routes/workflows/WorkflowRunParameters"; @@ -27,7 +25,7 @@ const router = createBrowserRouter([ children: [ { index: true, - element: , + element: , }, { path: "tasks", @@ -35,7 +33,21 @@ const router = createBrowserRouter([ children: [ { index: true, - element: , + element: , + }, + { + path: "create", + element: , + children: [ + { + path: ":template", + element: , + }, + { + path: "retry/:taskId", + element: , + }, + ], }, { path: ":taskId", @@ -65,24 +77,6 @@ const router = createBrowserRouter([ }, ], }, - { - path: "create", - element: , - children: [ - { - index: true, - element: , - }, - { - path: ":template", - element: , - }, - { - path: "retry/:taskId", - element: , - }, - ], - }, { path: "workflows", element: , diff --git a/skyvern-frontend/src/routes/root/SideNav.tsx b/skyvern-frontend/src/routes/root/SideNav.tsx index 97f8d0f5..9a6be348 100644 --- a/skyvern-frontend/src/routes/root/SideNav.tsx +++ b/skyvern-frontend/src/routes/root/SideNav.tsx @@ -1,75 +1,43 @@ +import { RobotIcon } from "@/components/icons/RobotIcon"; +import { NavLinkGroup } from "@/components/NavLinkGroup"; +import { useSidebarStore } from "@/store/SidebarStore"; import { cn } from "@/util/utils"; -import { - GearIcon, - LightningBoltIcon, - ListBulletIcon, - PlusCircledIcon, -} from "@radix-ui/react-icons"; -import { NavLink } from "react-router-dom"; +import { GearIcon, LightningBoltIcon } from "@radix-ui/react-icons"; -type Props = { - collapsed: boolean; -}; +function SideNav() { + const { collapsed } = useSidebarStore(); -function SideNav({ collapsed }: Props) { return ( -