diff --git a/client/dashboard/src/pages/toolsets/ToolSelect.tsx b/client/dashboard/src/pages/toolsets/ToolSelect.tsx index a193113b5..bc72af2c7 100644 --- a/client/dashboard/src/pages/toolsets/ToolSelect.tsx +++ b/client/dashboard/src/pages/toolsets/ToolSelect.tsx @@ -42,6 +42,8 @@ type ToggleableToolGroup = { key: string; defaultExpanded: boolean; toggleAll: () => void; + toggleEnableAll: () => void; + toggleDisableAll: () => void; tools: ToggleableTool[]; }; @@ -94,25 +96,33 @@ const groupColumnsToggleable: Column[] = [ width: "0.35fr", render: (row) => { const allEnabled = row.tools.every((t) => t.enabled); + const noneEnabled = row.tools.every((t) => !t.enabled); + + const onEnableAll = (e: React.MouseEvent) => { + e.stopPropagation(); + row.toggleEnableAll(); + }; + const onDisableAll = (e: React.MouseEvent) => { + e.stopPropagation(); + row.toggleDisableAll(); + }; return ( - + + Enable All + + + + ); }, }, @@ -194,6 +204,8 @@ export function ToolSelector({ toolsetSlug }: { toolsetSlug: string }) { const [selectedTools, setSelectedTools] = useState([]); const [search, setSearch] = useState(""); const [tagFilters, setTagFilters] = useState([]); + const [methodFilters, setMethodFilters] = useState([]); + const [enabledFilters, setEnabledFilters] = useState([]); const updateToolsetMutation = useUpdateToolsetMutation({ onSuccess: () => { @@ -252,6 +264,7 @@ export function ToolSelector({ toolsetSlug }: { toolsetSlug: string }) { const groupedTools = useGroupedHttpTools(tools?.tools ?? []); + const tagFilterOptions = groupedTools.flatMap((group) => group.tools.flatMap((t) => t.tags.map((tag) => `${group.key}/${tag}`)) ); @@ -269,6 +282,36 @@ export function ToolSelector({ toolsetSlug }: { toolsetSlug: string }) { /> ); + const methodOptions = useMemo(() => { + const methods = new Set(); + groupedTools.forEach((g) => g.tools.forEach((t) => methods.add((t.httpMethod || "OTHER").toUpperCase()))); + return Array.from(methods).sort().map((m) => ({ label: m, value: m })); + }, [groupedTools]); + + const methodsFilter = ( + setMethodFilters(vals.map((v) => v.toUpperCase()))} + placeholder="Filter by method" + className="w-fit capitalize" + /> + ); + + const enabledFilterItems = [ + { label: "Enabled", value: "ENABLED" }, + { label: "Disabled", value: "DISABLED" }, + ]; + + const enabledFilter = ( + + ) + + // Compose filtering: tag, search, method, enabled/disabled const filteredGroups = useMemo(() => { const normalize = (s: string) => s.toLowerCase().replace(/[^a-z0-9]/g, ""); const filteredGroups = groupedTools.map((g) => ({ @@ -280,6 +323,24 @@ export function ToolSelector({ toolsetSlug }: { toolsetSlug: string }) { ) { return false; } + + // method filter + if (methodFilters.length > 0) { + const method = (t.httpMethod || "OTHER").toUpperCase(); + if (!methodFilters.includes(method)) return false; + } + + // enabled/disabled filter + const isEnabled = selectedTools.includes(t.canonicalName); + const wantsEnabled = enabledFilters.includes("ENABLED"); + const wantsDisabled = enabledFilters.includes("DISABLED"); + + if (wantsEnabled && !wantsDisabled) { + if (!isEnabled) return false; + } else if (!wantsEnabled && wantsDisabled) { + if (isEnabled) return false; + } + const tags = t.tags.join(","); return ( normalize(t.name).includes(normalize(search)) || @@ -287,15 +348,16 @@ export function ToolSelector({ toolsetSlug }: { toolsetSlug: string }) { ); }), })); + return filteredGroups.filter((g) => g.tools.length > 0); - }, [tools, search, tagFilters]); + }, [tools, search, tagFilters, methodFilters, selectedTools, enabledFilters]); const toolGroups = useMemo(() => { - const toggleAll = (tools: ToggleableTool[]) => { - setToolsEnabled( - tools.map((t) => t.canonicalName), - tools.some((t) => !t.enabled) // Disable iff all are already enabled - ); + const enableAll = (tools: ToggleableTool[]) => { + setToolsEnabled(tools.map((t) => t.canonicalName), true); + }; + const disableAll = (tools: ToggleableTool[]) => { + setToolsEnabled(tools.map((t) => t.canonicalName), false); }; const toolGroups = filteredGroups.map((group) => ({ @@ -310,7 +372,8 @@ export function ToolSelector({ toolsetSlug }: { toolsetSlug: string }) { const toolGroupsFinal = toolGroups.map((group) => ({ ...group, - toggleAll: () => toggleAll(group.tools), + toggleEnableAll: () => enableAll(group.tools), + toggleDisableAll: () => disableAll(group.tools), defaultExpanded: toolGroups.length < 3 || group.tools.some((tool) => tool.enabled), })); @@ -338,6 +401,8 @@ export function ToolSelector({ toolsetSlug }: { toolsetSlug: string }) { <> {tagsFilter} + {methodsFilter} + {enabledFilter}