diff --git a/ui/admin/app/components/composed/typography.tsx b/ui/admin/app/components/composed/typography.tsx index 97b8864fc..29a8a2366 100644 --- a/ui/admin/app/components/composed/typography.tsx +++ b/ui/admin/app/components/composed/typography.tsx @@ -11,10 +11,12 @@ import { export function Truncate({ children, className, + classNames, asChild, disableTooltip, tooltipContent = children, clamp = true, + clampLength = 1, }: { children: React.ReactNode; className?: string; @@ -22,11 +24,25 @@ export function Truncate({ disableTooltip?: boolean; tooltipContent?: React.ReactNode; clamp?: boolean; + clampLength?: 1 | 2 | 3; + classNames?: { + root?: string; + }; }) { const Comp = asChild ? Slot : "p"; const content = ( - + {children} ); diff --git a/ui/admin/app/components/tools/list/ToolCard.tsx b/ui/admin/app/components/tools/list/ToolCard.tsx index 4f8418c3a..434413270 100644 --- a/ui/admin/app/components/tools/list/ToolCard.tsx +++ b/ui/admin/app/components/tools/list/ToolCard.tsx @@ -1,8 +1,9 @@ import { ToolReference } from "~/lib/model/toolReferences"; import { cn } from "~/lib/utils/cn"; +import { Truncate } from "~/components/composed/typography"; import { ToolIcon } from "~/components/tools/ToolIcon"; -import { ToolDropdown } from "~/components/tools/list/ToolDropdown"; +import { ToolCardActions } from "~/components/tools/list/ToolCardActions"; import { Button } from "~/components/ui/button"; import { Card, CardContent, CardHeader } from "~/components/ui/card"; import { @@ -26,11 +27,7 @@ export function ToolCard({ })} > - {!tool.builtin || tool?.metadata?.oauth ? ( - - ) : ( -
- )} +
{tool.error ? ( @@ -55,21 +52,33 @@ export function ToolCard({ )}
- + -
- {tool.name} -
-

{tool.description}

+ {tool.name} + + {tool.description} + {!tool.builtin && tool.reference && ( -

+ {tool.reference} -

+ )}
diff --git a/ui/admin/app/components/tools/list/ToolDropdown.tsx b/ui/admin/app/components/tools/list/ToolCardActions.tsx similarity index 77% rename from ui/admin/app/components/tools/list/ToolDropdown.tsx rename to ui/admin/app/components/tools/list/ToolCardActions.tsx index 99bb80b46..8121e7ec1 100644 --- a/ui/admin/app/components/tools/list/ToolDropdown.tsx +++ b/ui/admin/app/components/tools/list/ToolCardActions.tsx @@ -6,10 +6,12 @@ import { mutate } from "swr"; import { OAuthProvider } from "~/lib/model/oauthApps/oauth-helpers"; import { ToolReference } from "~/lib/model/toolReferences"; import { ToolReferenceService } from "~/lib/service/api/toolreferenceService"; +import { cn } from "~/lib/utils"; import { ConfirmationDialog } from "~/components/composed/ConfirmationDialog"; import { CustomOauthAppDetail } from "~/components/oauth-apps/CustomOauthAppDetail"; import { OAuthAppDetail } from "~/components/oauth-apps/OAuthAppDetail"; +import { LoadingSpinner } from "~/components/ui/LoadingSpinner"; import { Button } from "~/components/ui/button"; import { DropdownMenu, @@ -20,12 +22,9 @@ import { import { useConfirmationDialog } from "~/hooks/component-helpers/useConfirmationDialog"; import { useOAuthAppList } from "~/hooks/oauthApps/useOAuthApps"; import { useAsync } from "~/hooks/useAsync"; +import { usePollSingleTool } from "~/hooks/usePollSingleTool"; -type ToolDropdownProps = { - tool: ToolReference; -}; - -export function ToolDropdown({ tool }: ToolDropdownProps) { +export function ToolCardActions({ tool }: { tool: ToolReference }) { const [configureAuthOpen, setConfigureAuthOpen] = useState(false); const { dialogProps, interceptAsync } = useConfirmationDialog(); @@ -38,13 +37,25 @@ export function ToolDropdown({ tool }: ToolDropdownProps) { const deleteTool = useAsync(ToolReferenceService.deleteToolReference, { onSuccess: () => { toast.success("Tool has been deleted."); - mutate(ToolReferenceService.getToolReferencesCategoryMap.key("tool")); + mutate(ToolReferenceService.getToolReferences.key("tool")); }, onError: () => { toast.error("Something went wrong"); }, }); + const { startPolling, isPolling } = usePollSingleTool(tool.id); + + const forceRefresh = useAsync( + ToolReferenceService.forceRefreshToolReference, + { + onSuccess: () => { + toast.success("Tool reference force refreshed"); + startPolling(); + }, + } + ); + const handleDelete = () => interceptAsync(() => deleteTool.executeAsync(tool.id)); @@ -60,17 +71,21 @@ export function ToolDropdown({ tool }: ToolDropdownProps) { - + {!tool.error && toolOauthMetadata && ( { setConfigureAuthOpen(true); }} @@ -81,6 +96,9 @@ export function ToolDropdown({ tool }: ToolDropdownProps) { Configure OAuth )} + forceRefresh.execute(tool.id)}> + Refresh Tool + {!tool.builtin && ( void; -} - -export function ToolCard({ tool, onDelete }: ToolCardProps) { - return ( - - -

-
- - {tool.name} -
- {tool.error && ( - - - - Failed - - - -

{tool.error}

-
-
- )} - {tool.metadata?.bundle && ( - Bundle - )} -

- - -
- - {!tool.builtin && ( - {tool.reference} - )} -

- {tool.description || "No description available"} -

-
- - - {timeSince(new Date(tool.created))} ago - - - {!tool.builtin && ( - onDelete(tool.id)} - confirmProps={{ - variant: "destructive", - children: "Delete", - }} - > - - - )} - -
- ); -} diff --git a/ui/admin/app/components/tools/toolGrid/ToolGrid.tsx b/ui/admin/app/components/tools/toolGrid/ToolGrid.tsx deleted file mode 100644 index fd0f17504..000000000 --- a/ui/admin/app/components/tools/toolGrid/ToolGrid.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import { useCallback, useEffect, useState } from "react"; - -import { - CustomToolsToolCategory, - ToolCategoryMap, - ToolReference, -} from "~/lib/model/toolReferences"; - -import { CategoryHeader } from "~/components/tools/toolGrid/CategoryHeader"; -import { CategoryTools } from "~/components/tools/toolGrid/CategoryTools"; -import { useDebounce } from "~/hooks/useDebounce"; - -interface ToolGridProps { - toolCategories: ToolCategoryMap; - filter: string; - onDelete: (id: string) => void; -} - -export function ToolGrid({ toolCategories, filter, onDelete }: ToolGridProps) { - const [filteredResults, setFilteredResults] = - useState(toolCategories); - - const filterCategories = useCallback( - (searchTerm: string) => { - const result: ToolCategoryMap = {}; - for (const [category, { tools, bundleTool }] of Object.entries( - toolCategories - )) { - const sortedTools = tools.sort((a, b) => a.name.localeCompare(b.name)); - const toolsWithBundle = bundleTool - ? [bundleTool, ...sortedTools] - : sortedTools; - const filteredTools = toolsWithBundle.filter((tool) => - [tool.name, tool.metadata?.category, tool.description] - .filter((x) => !!x) - .join("|") - .toLowerCase() - .includes(searchTerm.toLowerCase()) - ); - if (filteredTools.length > 0) { - result[category] = { - tools: filteredTools, - bundleTool: bundleTool, - }; - } - } - setFilteredResults(result); - }, - [toolCategories] - ); - - const debouncedFilter = useDebounce(filterCategories, 150); - - useEffect(() => { - debouncedFilter(filter); - }, [filter, debouncedFilter]); - - if (!Object.entries(filteredResults).length) { - return

No tools found...

; - } - - const customToolsCategory = filteredResults[CustomToolsToolCategory]; - return ( -
- {customToolsCategory && - renderToolCategory(CustomToolsToolCategory, customToolsCategory.tools)} - {Object.entries(filteredResults).map( - ([category, { tools, bundleTool }]) => { - if (category === CustomToolsToolCategory) return null; - return renderToolCategory(category, tools, bundleTool?.description); - } - )} -
- ); - - function renderToolCategory( - category: string, - tools: ToolReference[], - description = "" - ) { - if (!tools.length) return null; - return ( -
- - -
- ); - } -}