From 66b24dd13e461cead224acb005e45304f94c2de1 Mon Sep 17 00:00:00 2001 From: Ajax Davis Date: Thu, 11 Dec 2025 09:27:16 +1000 Subject: [PATCH] feat(tool-search): add sort dropdown with Most Downloaded and Recent options - Add sortBy state with 'downloads' as default (most downloaded) - Add 'recent' sort option to sort by createdAt - Sorting keeps broken tools at bottom regardless of sort order - Add createdAt field to Tool interface --- apps/web/src/app/tool/tool-search/page.tsx | 32 +++++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/apps/web/src/app/tool/tool-search/page.tsx b/apps/web/src/app/tool/tool-search/page.tsx index 9b696d2..a9c59d7 100644 --- a/apps/web/src/app/tool/tool-search/page.tsx +++ b/apps/web/src/app/tool/tool-search/page.tsx @@ -21,6 +21,7 @@ interface Tool { qualityScore: string; importHealth?: 'HEALTHY' | 'BROKEN' | 'UNKNOWN'; executionHealth?: 'HEALTHY' | 'BROKEN' | 'UNKNOWN'; + createdAt: string; package: { npmPackageName: string; npmVersion: string; @@ -31,6 +32,8 @@ interface Tool { }; } +type SortOption = 'downloads' | 'recent'; + /** * Tool Registry Search Page * @@ -40,6 +43,7 @@ export default function ToolSearchPage(): React.ReactElement { const [searchQuery, setSearchQuery] = useState(''); const [categoryFilter, setCategoryFilter] = useState('all'); const [healthFilter, setHealthFilter] = useState('all'); + const [sortBy, setSortBy] = useState('downloads'); const [tools, setTools] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); @@ -75,13 +79,19 @@ export default function ToolSearchPage(): React.ReactElement { if (toolsData.success) { const fetchedTools = toolsData.data; - // Sort broken tools to the bottom - const sortedTools = [...fetchedTools].sort((a, b) => { + // Sort by selected criterion, then push broken tools to bottom + const sortedTools = [...fetchedTools].sort((a: Tool, b: Tool) => { const aIsBroken = a.importHealth === 'BROKEN' || a.executionHealth === 'BROKEN'; const bIsBroken = b.importHealth === 'BROKEN' || b.executionHealth === 'BROKEN'; + // Always push broken tools to bottom if (aIsBroken && !bIsBroken) return 1; if (!aIsBroken && bIsBroken) return -1; - return 0; + // Within same broken status, sort by selected criterion + if (sortBy === 'downloads') { + return b.package.npmDownloadsLastMonth - a.package.npmDownloadsLastMonth; + } + // Sort by recent (createdAt descending) + return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(); }); setTools(sortedTools); setError(null); @@ -107,7 +117,7 @@ export default function ToolSearchPage(): React.ReactElement { }; fetchTools(); - }, [searchQuery, categoryFilter, healthFilter]); + }, [searchQuery, categoryFilter, healthFilter, sortBy]); return (
@@ -166,6 +176,20 @@ export default function ToolSearchPage(): React.ReactElement { />
+ {/* Sort dropdown */} +
+ Sort: +