From f2b5a4a1f8a41dc563f27ff544833502dc0c0a09 Mon Sep 17 00:00:00 2001 From: EthanHealy01 Date: Fri, 15 Aug 2025 18:42:56 +0100 Subject: [PATCH] simplify search results & tool picker --- .../src/components/tools/SearchResults.tsx | 46 ++++--------------- frontend/src/components/tools/ToolPicker.tsx | 16 +------ .../tools/shared/SubcategoryHeader.tsx | 17 +++++++ 3 files changed, 26 insertions(+), 53 deletions(-) create mode 100644 frontend/src/components/tools/shared/SubcategoryHeader.tsx diff --git a/frontend/src/components/tools/SearchResults.tsx b/frontend/src/components/tools/SearchResults.tsx index 48f238dde..55755bf6f 100644 --- a/frontend/src/components/tools/SearchResults.tsx +++ b/frontend/src/components/tools/SearchResults.tsx @@ -3,6 +3,8 @@ import { Box, Stack, Text } from '@mantine/core'; import { type ToolRegistryEntry } from '../../data/toolRegistry'; import ToolButton from './toolPicker/ToolButton'; import { useTranslation } from 'react-i18next'; +import { useToolSections } from '../../hooks/useToolSections'; +import SubcategoryHeader from './shared/SubcategoryHeader'; interface SearchResultsProps { filteredTools: [string, ToolRegistryEntry][]; @@ -11,38 +13,9 @@ interface SearchResultsProps { const SearchResults: React.FC = ({ filteredTools, onSelect }) => { const { t } = useTranslation(); - // Group tools by subcategory and remove duplicates - const groupedToolsByCategory = useMemo(() => { - const categoryToToolsMap: Record> = {}; - const processedToolIds = new Set(); + const { searchGroups } = useToolSections(filteredTools); - // Process each tool, skipping duplicates and grouping by subcategory - filteredTools.forEach(([toolId, toolEntry]) => { - // Skip if we've already processed this tool ID (deduplication) - if (processedToolIds.has(toolId)) return; - processedToolIds.add(toolId); - - // Use subcategory or default to 'General' if not specified - const categoryName = toolEntry?.subcategory || 'General'; - - // Initialize category array if it doesn't exist - if (!categoryToToolsMap[categoryName]) { - categoryToToolsMap[categoryName] = []; - } - - categoryToToolsMap[categoryName].push({ id: toolId, tool: toolEntry }); - }); - - // Convert to sorted array format for rendering - return Object.entries(categoryToToolsMap) - .sort(([categoryA], [categoryB]) => categoryA.localeCompare(categoryB)) - .map(([categoryName, toolsInCategory]) => ({ - categoryName, - toolsInCategory - })); - }, [filteredTools]); - - if (groupedToolsByCategory.length === 0) { + if (searchGroups.length === 0) { return ( {t('toolPicker.noToolsFound', 'No tools found')} @@ -52,13 +25,11 @@ const SearchResults: React.FC = ({ filteredTools, onSelect } return ( - {groupedToolsByCategory.map(categoryGroup => ( - - - {t(`toolPicker.subcategories.${categoryGroup.categoryName}`, categoryGroup.categoryName)} - + {searchGroups.map(group => ( + + - {categoryGroup.toolsInCategory.map(({ id, tool }) => ( + {group.tools.map(({ id, tool }) => ( = ({ filteredTools, onSelect } /> ))} - {/* bottom spacer within each group not strictly required, outer list can add a spacer if needed */} ))} {/* global spacer to allow scrolling past last row in search mode */} diff --git a/frontend/src/components/tools/ToolPicker.tsx b/frontend/src/components/tools/ToolPicker.tsx index 39ed8ad5c..eb2c8b3c6 100644 --- a/frontend/src/components/tools/ToolPicker.tsx +++ b/frontend/src/components/tools/ToolPicker.tsx @@ -5,6 +5,7 @@ import { type ToolRegistryEntry } from "../../data/toolRegistry"; import ToolButton from "./toolPicker/ToolButton"; import "./toolPicker/ToolPicker.css"; import { useToolSections } from "../../hooks/useToolSections"; +import SubcategoryHeader from "./shared/SubcategoryHeader"; interface ToolPickerProps { selectedToolKey: string | null; @@ -13,12 +14,6 @@ interface ToolPickerProps { isSearching?: boolean; } -interface GroupedTools { - [category: string]: { - [subcategory: string]: Array<{ id: string; tool: ToolRegistryEntry }>; - }; -} - const ToolPicker = ({ selectedToolKey, onSelect, filteredTools, isSearching = false }: ToolPickerProps) => { const { t } = useTranslation(); const [quickHeaderHeight, setQuickHeaderHeight] = useState(0); @@ -45,15 +40,6 @@ const ToolPicker = ({ selectedToolKey, onSelect, filteredTools, isSearching = fa return () => window.removeEventListener("resize", update); }, []); - // Lightweight inline component to avoid an extra file - const SubcategoryHeader: React.FC<{ label: string; mt?: string | number; mb?: string | number }> = ({ label, mt = "1rem", mb = "0.25rem" }) => ( -
-
- {label} -
-
- ); - const { sections: visibleSections } = useToolSections(filteredTools); const quickSection = useMemo( diff --git a/frontend/src/components/tools/shared/SubcategoryHeader.tsx b/frontend/src/components/tools/shared/SubcategoryHeader.tsx new file mode 100644 index 000000000..b403ebae8 --- /dev/null +++ b/frontend/src/components/tools/shared/SubcategoryHeader.tsx @@ -0,0 +1,17 @@ +import React from 'react'; + +interface SubcategoryHeaderProps { + label: string; + mt?: string | number; + mb?: string | number; +} + +export const SubcategoryHeader: React.FC = ({ label, mt = '1rem', mb = '0.25rem' }) => ( +
+
+ {label} +
+
+); + +export default SubcategoryHeader;