diff --git a/packages/ui/src/components/Catalog/filter-tiles.ts b/packages/ui/src/components/Catalog/filter-tiles.ts index f7c10d641..9e850f9c2 100644 --- a/packages/ui/src/components/Catalog/filter-tiles.ts +++ b/packages/ui/src/components/Catalog/filter-tiles.ts @@ -1,3 +1,4 @@ +import { CatalogKind } from '../../models'; import { ITile } from './Catalog.models'; const checkThatArrayContainsAllTags = (tileTags: string[], searchTags: string[]) => @@ -10,37 +11,65 @@ export const filterTiles = ( const { searchTerm = '', searchTags = [], selectedProviders = [] } = options ?? {}; const searchTermLowercase = searchTerm.toLowerCase(); - return tiles.reduce( - (acc, tile) => { - /** Filter by selected tags */ - const doesTagsMatches = searchTags.length ? checkThatArrayContainsAllTags(tile.tags, searchTags) : true; - - /** Filter by providers */ - let doesProviderMatch = true; - if (selectedProviders.length) { - doesProviderMatch = - tile.provider === undefined - ? selectedProviders.includes('Community') - : selectedProviders.includes(tile.provider); - } - - /** Determine whether the tile should be included in the filtered list */ - const shouldInclude = - doesTagsMatches && - doesProviderMatch && - (!searchTermLowercase || - tile.name.toLowerCase().includes(searchTermLowercase) || - tile.title.toLowerCase().includes(searchTermLowercase) || - tile.description?.toLowerCase().includes(searchTermLowercase) || - tile.tags.some((tag) => tag.toLowerCase().includes(searchTermLowercase))); - - acc[tile.type] = acc[tile.type] ?? []; - if (shouldInclude) { - acc[tile.type].push(tile); - } - - return acc; - }, - {} as Record, - ); + // Step 1: Score each tile based on how well it matches the search term + const scoredTiles = tiles.map((tile) => { + let score = 0; + + // Score based on name + const nameLower = tile.name.toLowerCase(); + if (nameLower.startsWith(searchTermLowercase)) { + score += 100; + } else if (nameLower.includes(searchTermLowercase)) { + score += 40; + } + + // Score based on title + if (tile.title?.toLowerCase().includes(searchTermLowercase)) { + score += 40; + } + + // Score based on description + if (tile.description?.toLowerCase().includes(searchTermLowercase)) { + score += 10; + } + + return { tile, score }; + }); + + // Step 2: Filter tiles based on score, tags, and providers + const filteredTiles = scoredTiles.filter(({ tile, score }) => { + // Exclude tiles with no match + if (score <= 0) return false; + + // Filter by selected tags + const doesTagsMatch = searchTags.length ? checkThatArrayContainsAllTags(tile.tags, searchTags) : true; + + // Filter by selected providers + let doesProviderMatch = true; + if (selectedProviders.length) { + doesProviderMatch = + tile.provider === undefined + ? selectedProviders.includes('Community') + : selectedProviders.includes(tile.provider); + } + + return doesTagsMatch && doesProviderMatch; + }); + + // Step 3: Sort the filtered tiles by score in descending order + filteredTiles.sort((a, b) => b.score - a.score); + + // Step 4: Group the sorted tiles by their type + const groupedTiles: Record = {}; + groupedTiles[CatalogKind.Component] = []; + groupedTiles[CatalogKind.Kamelet] = []; + groupedTiles[CatalogKind.Pattern] = []; + filteredTiles.forEach(({ tile }) => { + if (!groupedTiles[tile.type]) { + groupedTiles[tile.type] = []; + } + groupedTiles[tile.type].push(tile); + }); + + return groupedTiles; };