simplify search results & tool picker

This commit is contained in:
EthanHealy01 2025-08-15 18:42:56 +01:00
parent 19d2b72f65
commit f2b5a4a1f8
3 changed files with 26 additions and 53 deletions

View File

@ -3,6 +3,8 @@ import { Box, Stack, Text } from '@mantine/core';
import { type ToolRegistryEntry } from '../../data/toolRegistry'; import { type ToolRegistryEntry } from '../../data/toolRegistry';
import ToolButton from './toolPicker/ToolButton'; import ToolButton from './toolPicker/ToolButton';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useToolSections } from '../../hooks/useToolSections';
import SubcategoryHeader from './shared/SubcategoryHeader';
interface SearchResultsProps { interface SearchResultsProps {
filteredTools: [string, ToolRegistryEntry][]; filteredTools: [string, ToolRegistryEntry][];
@ -11,38 +13,9 @@ interface SearchResultsProps {
const SearchResults: React.FC<SearchResultsProps> = ({ filteredTools, onSelect }) => { const SearchResults: React.FC<SearchResultsProps> = ({ filteredTools, onSelect }) => {
const { t } = useTranslation(); const { t } = useTranslation();
// Group tools by subcategory and remove duplicates const { searchGroups } = useToolSections(filteredTools);
const groupedToolsByCategory = useMemo(() => {
const categoryToToolsMap: Record<string, Array<{ id: string; tool: ToolRegistryEntry }>> = {};
const processedToolIds = new Set<string>();
// Process each tool, skipping duplicates and grouping by subcategory if (searchGroups.length === 0) {
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) {
return ( return (
<Text c="dimmed" size="sm" p="sm"> <Text c="dimmed" size="sm" p="sm">
{t('toolPicker.noToolsFound', 'No tools found')} {t('toolPicker.noToolsFound', 'No tools found')}
@ -52,13 +25,11 @@ const SearchResults: React.FC<SearchResultsProps> = ({ filteredTools, onSelect }
return ( return (
<Stack p="sm" gap="xs"> <Stack p="sm" gap="xs">
{groupedToolsByCategory.map(categoryGroup => ( {searchGroups.map(group => (
<Box key={categoryGroup.categoryName} w="100%"> <Box key={group.subcategory} w="100%">
<Text size="sm" fw={500} mb="0.25rem" mt="1rem" className="tool-subcategory-title"> <SubcategoryHeader label={t(`toolPicker.subcategories.${group.subcategory}`, group.subcategory)} />
{t(`toolPicker.subcategories.${categoryGroup.categoryName}`, categoryGroup.categoryName)}
</Text>
<Stack gap="xs"> <Stack gap="xs">
{categoryGroup.toolsInCategory.map(({ id, tool }) => ( {group.tools.map(({ id, tool }) => (
<ToolButton <ToolButton
key={id} key={id}
id={id} id={id}
@ -68,7 +39,6 @@ const SearchResults: React.FC<SearchResultsProps> = ({ filteredTools, onSelect }
/> />
))} ))}
</Stack> </Stack>
{/* bottom spacer within each group not strictly required, outer list can add a spacer if needed */}
</Box> </Box>
))} ))}
{/* global spacer to allow scrolling past last row in search mode */} {/* global spacer to allow scrolling past last row in search mode */}

View File

@ -5,6 +5,7 @@ import { type ToolRegistryEntry } from "../../data/toolRegistry";
import ToolButton from "./toolPicker/ToolButton"; import ToolButton from "./toolPicker/ToolButton";
import "./toolPicker/ToolPicker.css"; import "./toolPicker/ToolPicker.css";
import { useToolSections } from "../../hooks/useToolSections"; import { useToolSections } from "../../hooks/useToolSections";
import SubcategoryHeader from "./shared/SubcategoryHeader";
interface ToolPickerProps { interface ToolPickerProps {
selectedToolKey: string | null; selectedToolKey: string | null;
@ -13,12 +14,6 @@ interface ToolPickerProps {
isSearching?: boolean; isSearching?: boolean;
} }
interface GroupedTools {
[category: string]: {
[subcategory: string]: Array<{ id: string; tool: ToolRegistryEntry }>;
};
}
const ToolPicker = ({ selectedToolKey, onSelect, filteredTools, isSearching = false }: ToolPickerProps) => { const ToolPicker = ({ selectedToolKey, onSelect, filteredTools, isSearching = false }: ToolPickerProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [quickHeaderHeight, setQuickHeaderHeight] = useState(0); const [quickHeaderHeight, setQuickHeaderHeight] = useState(0);
@ -45,15 +40,6 @@ const ToolPicker = ({ selectedToolKey, onSelect, filteredTools, isSearching = fa
return () => window.removeEventListener("resize", update); 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" }) => (
<div className="tool-subcategory-row" style={{ marginLeft: "1rem", marginRight: "1rem", marginTop: mt, marginBottom: mb }}>
<div className="tool-subcategory-row-rule" />
<span className="tool-subcategory-row-title">{label}</span>
<div className="tool-subcategory-row-rule" />
</div>
);
const { sections: visibleSections } = useToolSections(filteredTools); const { sections: visibleSections } = useToolSections(filteredTools);
const quickSection = useMemo( const quickSection = useMemo(

View File

@ -0,0 +1,17 @@
import React from 'react';
interface SubcategoryHeaderProps {
label: string;
mt?: string | number;
mb?: string | number;
}
export const SubcategoryHeader: React.FC<SubcategoryHeaderProps> = ({ label, mt = '1rem', mb = '0.25rem' }) => (
<div className="tool-subcategory-row" style={{ marginLeft: '1rem', marginRight: '1rem', marginTop: mt, marginBottom: mb }}>
<div className="tool-subcategory-row-rule" />
<span className="tool-subcategory-row-title">{label}</span>
<div className="tool-subcategory-row-rule" />
</div>
);
export default SubcategoryHeader;