From a9cc9db44ffb9029306b4ab3f9c502529d8d3145 Mon Sep 17 00:00:00 2001 From: tylerslaton Date: Wed, 23 Oct 2024 11:42:26 -0400 Subject: [PATCH] feat: add tools table for creating and deleting tools Signed-off-by: tylerslaton --- ui/admin/app/components/header/HeaderNav.tsx | 5 + ui/admin/app/components/sidebar/Sidebar.tsx | 6 ++ ui/admin/app/components/tools/CreateTool.tsx | 56 ++++++++++ ui/admin/app/components/tools/ToolTable.tsx | 104 +++++++++++++++++++ ui/admin/app/routes/_auth.tools._index.tsx | 91 ++++++++++++++++ 5 files changed, 262 insertions(+) create mode 100644 ui/admin/app/components/tools/CreateTool.tsx create mode 100644 ui/admin/app/components/tools/ToolTable.tsx create mode 100644 ui/admin/app/routes/_auth.tools._index.tsx diff --git a/ui/admin/app/components/header/HeaderNav.tsx b/ui/admin/app/components/header/HeaderNav.tsx index ca9254bc..861d27a9 100644 --- a/ui/admin/app/components/header/HeaderNav.tsx +++ b/ui/admin/app/components/header/HeaderNav.tsx @@ -104,6 +104,11 @@ function getBreadcrumbs(route: string, params: Readonly>) { )} + {new RegExp($path("/tools")).test(route) && ( + + Tools + + )} {new RegExp($path("/users")).test(route) && ( Users diff --git a/ui/admin/app/components/sidebar/Sidebar.tsx b/ui/admin/app/components/sidebar/Sidebar.tsx index cb666a82..83963c35 100644 --- a/ui/admin/app/components/sidebar/Sidebar.tsx +++ b/ui/admin/app/components/sidebar/Sidebar.tsx @@ -5,6 +5,7 @@ import { MessageSquare, SettingsIcon, User, + Wrench, } from "lucide-react"; import { $path } from "remix-routes"; @@ -44,6 +45,11 @@ const items = [ url: $path("/threads"), icon: MessageSquare, }, + { + title: "Tools", + url: $path("/tools"), + icon: Wrench, + }, { title: "Users", url: $path("/users"), diff --git a/ui/admin/app/components/tools/CreateTool.tsx b/ui/admin/app/components/tools/CreateTool.tsx new file mode 100644 index 00000000..e5020037 --- /dev/null +++ b/ui/admin/app/components/tools/CreateTool.tsx @@ -0,0 +1,56 @@ +import { useForm } from "react-hook-form"; + +import { CreateToolReference } from "~/lib/model/toolReferences"; +import { ToolReferenceService } from "~/lib/service/api/toolreferenceService"; + +import { Button } from "~/components/ui/button"; +import { Input } from "~/components/ui/input"; +import { useAsync } from "~/hooks/useAsync"; + +interface CreateToolProps { + onSuccess: () => void; +} + +export function CreateTool({ onSuccess }: CreateToolProps) { + const { register, handleSubmit, reset } = useForm(); + + const { execute: onSubmit, isLoading } = useAsync( + async (data: CreateToolReference) => { + await ToolReferenceService.createToolReference({ + toolReference: { ...data, toolType: "tool" }, + }); + reset(); + onSuccess(); + }, + { + onError: (error) => + console.error("Failed to create tool reference:", error), + } + ); + + return ( +
+
+ +
+
+ +
+
+ +
+
+ ); +} diff --git a/ui/admin/app/components/tools/ToolTable.tsx b/ui/admin/app/components/tools/ToolTable.tsx new file mode 100644 index 00000000..2bb4aee6 --- /dev/null +++ b/ui/admin/app/components/tools/ToolTable.tsx @@ -0,0 +1,104 @@ +import { ColumnDef, createColumnHelper } from "@tanstack/react-table"; +import { Trash } from "lucide-react"; + +import { ToolReference } from "~/lib/model/toolReferences"; +import { timeSince } from "~/lib/utils"; + +import { TypographyP } from "~/components/Typography"; +import { ConfirmationDialog } from "~/components/composed/ConfirmationDialog"; +import { DataTable } from "~/components/composed/DataTable"; +import { ToolIcon } from "~/components/tools/ToolIcon"; +import { Button } from "~/components/ui/button"; + +interface ToolTableProps { + tools: ToolReference[]; + filter: string; + onDelete: (id: string) => void; +} + +export function ToolTable({ tools, filter, onDelete }: ToolTableProps) { + const filteredTools = tools.filter( + (tool) => + tool.name?.toLowerCase().includes(filter.toLowerCase()) || + tool.metadata?.category + ?.toLowerCase() + .includes(filter.toLowerCase()) || + tool.description?.toLowerCase().includes(filter.toLowerCase()) + ); + + return ( + + ); +} + +function getColumns( + onDelete: (id: string) => void +): ColumnDef[] { + const columnHelper = createColumnHelper(); + + return [ + columnHelper.display({ + id: "category", + header: "Category", + cell: ({ row }) => ( + + + {row.original.metadata?.category ?? "Uncategorized"} + + ), + }), + columnHelper.display({ + id: "name", + header: "Name", + cell: ({ row }) => ( + + {row.original.name} + {row.original.metadata?.bundle ? " Bundle" : ""} + + ), + }), + columnHelper.accessor("reference", { + header: "Reference", + }), + columnHelper.display({ + id: "description", + header: "Description", + cell: ({ row }) => ( + {row.original.description} + ), + }), + columnHelper.accessor("created", { + header: "Created", + cell: ({ getValue }) => ( + {timeSince(new Date(getValue()))} ago + ), + sortingFn: "datetime", + }), + columnHelper.display({ + id: "actions", + cell: ({ row }) => ( + onDelete(row.original.id)} + confirmProps={{ + variant: "destructive", + children: "Delete", + }} + > + + + ), + }), + ]; +} diff --git a/ui/admin/app/routes/_auth.tools._index.tsx b/ui/admin/app/routes/_auth.tools._index.tsx new file mode 100644 index 00000000..ef193b5e --- /dev/null +++ b/ui/admin/app/routes/_auth.tools._index.tsx @@ -0,0 +1,91 @@ +import { PlusIcon, SearchIcon } from "lucide-react"; +import { useState } from "react"; +import useSWR, { preload } from "swr"; + +import { ToolReferenceService } from "~/lib/service/api/toolreferenceService"; + +import { TypographyH2 } from "~/components/Typography"; +import { CreateTool } from "~/components/tools/CreateTool"; +import { ToolTable } from "~/components/tools/ToolTable"; +import { Button } from "~/components/ui/button"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "~/components/ui/dialog"; +import { Input } from "~/components/ui/input"; + +export async function clientLoader() { + await Promise.all([ + preload(ToolReferenceService.getToolReferences.key("tool"), () => + ToolReferenceService.getToolReferences("tool") + ), + ]); + return null; +} + +export default function Tools() { + const { data: tools, mutate } = useSWR( + ToolReferenceService.getToolReferences.key("tool"), + () => ToolReferenceService.getToolReferences("tool") + ); + + const [isDialogOpen, setIsDialogOpen] = useState(false); + const [searchQuery, setSearchQuery] = useState(""); + + const handleCreateSuccess = () => { + mutate(); + setIsDialogOpen(false); + }; + + const handleDelete = async (id: string) => { + await ToolReferenceService.deleteToolReference(id); + mutate(); + }; + + return ( +
+
+ Tools +
+
+ + setSearchQuery(e.target.value)} + className="pl-10 w-64" + /> +
+ + + + + + + + Create New Tool Reference + + + + + +
+
+ + {tools && ( + + )} +
+ ); +}