From c672225ed8b842239fc1c0204934f3ab24e02dfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C5=A1per=20Grom?= Date: Mon, 20 Jul 2026 20:58:48 +0100 Subject: [PATCH] feat: add organization results to global search bar IN-1206 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend the global search bar to return organizations alongside projects, repositories, and collections. Each org result shows its logo (with an org-icon fallback when absent) and name, and links to /organization/{slug}. Organizations appear as the last section/tab. Org data comes from the search_collections_projects_repos Tinybird pipe's new 'organization' rows; the datasource is pre-filtered to orgs with linked activity. Requires the crowd.dev Tinybird pipe change to be deployed first. Claude-Session: https://claude.ai/code/session_01JFS7TVQUvsTBRyuihHjtQX Signed-off-by: Gašper Grom --- .../services/collections.api.service.ts | 2 +- .../shared/layout/search/search-modal.vue | 14 ++++- .../shared/layout/search/search-result.vue | 53 ++++++++++++++++++- .../shared/layout/search/search.vue | 2 +- frontend/server/api/search.ts | 25 +++++++-- frontend/types/search.ts | 7 +++ 6 files changed, 92 insertions(+), 11 deletions(-) diff --git a/frontend/app/components/modules/collection/services/collections.api.service.ts b/frontend/app/components/modules/collection/services/collections.api.service.ts index 26a2ff599..c03793069 100644 --- a/frontend/app/components/modules/collection/services/collections.api.service.ts +++ b/frontend/app/components/modules/collection/services/collections.api.service.ts @@ -209,7 +209,7 @@ class CollectionsApiService { async searchProjectsAndRepositories(query: string): Promise { const sanitizedQuery = sanitizeSearchQuery(query); if (!sanitizedQuery) { - return { projects: [], repositories: [], collections: [] }; + return { projects: [], repositories: [], collections: [], organizations: [] }; } const res = await $fetch('/api/search', { diff --git a/frontend/app/components/shared/layout/search/search-modal.vue b/frontend/app/components/shared/layout/search/search-modal.vue index f8226bb16..00281a593 100644 --- a/frontend/app/components/shared/layout/search/search-modal.vue +++ b/frontend/app/components/shared/layout/search/search-modal.vue @@ -17,7 +17,7 @@ SPDX-License-Identifier: MIT v-model="search" type="text" class="!outline-none !shadow-none flex-grow text-sm text-neutral-900 leading-5" - placeholder="Search projects, repositories, or collections..." + placeholder="Search projects, repositories, collections, or organizations..." @input="triggerSearch" /> @@ -58,7 +59,13 @@ SPDX-License-Identifier: MIT diff --git a/frontend/app/components/shared/layout/search/search.vue b/frontend/app/components/shared/layout/search/search.vue index 48d1de3ae..b26363d2f 100644 --- a/frontend/app/components/shared/layout/search/search.vue +++ b/frontend/app/components/shared/layout/search/search.vue @@ -13,7 +13,7 @@ SPDX-License-Identifier: MIT class="text-neutral-400 font-normal" :size="14" /> -

Search projects, repositories, or collections

+

Search projects, repositories, collections, or organizations

): A list of search results of type "project". * - repositories (Array): A list of search results of type "repository". * - collections (Array): A list of search results of type "collection". + * - organizations (Array): A list of search results of type "organization". * * Search Response Object (SearchResponse): - * - type (string): The type identifier for the search result (e.g., "project", "repository", "collection"). + * - type (string): The type identifier for the search result (e.g., "project", "repository", "collection", "organization"). * - slug (string): A unique slug identifier for the search result. * - logo (string | null): The logo URL associated with the search result, or null if unavailable. * - projectSlug (string | null): The project slug associated with the result, or null if absent. @@ -48,6 +54,7 @@ export default defineEventHandler(async (event) => { const projects: SearchProject[] = []; const repositories: SearchRepository[] = []; const collections: SearchCollection[] = []; + const organizations: SearchOrganization[] = []; const limit: number = 10; @@ -86,6 +93,12 @@ export default defineEventHandler(async (event) => { slug: item.slug, name: item.name || '', }); + } else if (item.type === 'organization') { + organizations.push({ + slug: item.slug, + name: item.name || '', + logo: item.logo, + }); } }); } @@ -94,6 +107,7 @@ export default defineEventHandler(async (event) => { projects, repositories, collections, + organizations, }; } catch (error) { console.error('Error fetching search results:', error); @@ -101,6 +115,7 @@ export default defineEventHandler(async (event) => { projects, repositories, collections, + organizations, }; } }); diff --git a/frontend/types/search.ts b/frontend/types/search.ts index 838c886d3..3406e8d31 100644 --- a/frontend/types/search.ts +++ b/frontend/types/search.ts @@ -22,8 +22,15 @@ export interface SearchRepository { url: string; } +export interface SearchOrganization { + name: string; + slug: string; + logo: string | null; +} + export interface SearchResults { projects: SearchProject[]; repositories: SearchRepository[]; collections: SearchCollection[]; + organizations: SearchOrganization[]; }