mirror of https://github.com/grafana/grafana.git
				
				
				
			SearchV2: add CTA and reporting to new search UI (#50175)
This commit is contained in:
		
							parent
							
								
									7a614fd8a1
								
							
						
					
					
						commit
						1a324b3330
					
				|  | @ -95,7 +95,7 @@ export const ActionRow: FC<Props> = ({ | |||
|             Datasource: {query.datasource} | ||||
|           </Button> | ||||
|         )} | ||||
|         {layout !== SearchLayout.Folders && ( | ||||
|         {layout !== SearchLayout.Folders && config.featureToggles.panelTitleSearch && ( | ||||
|           <Checkbox value={includePanels} onChange={() => setIncludePanels(!includePanels)} label="Include panels" /> | ||||
|         )} | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,11 +1,11 @@ | |||
| import { css } from '@emotion/css'; | ||||
| import React, { useCallback, useMemo, useState } from 'react'; | ||||
| import { useAsync } from 'react-use'; | ||||
| import { useAsync, useDebounce } from 'react-use'; | ||||
| import AutoSizer from 'react-virtualized-auto-sizer'; | ||||
| 
 | ||||
| import { GrafanaTheme2 } from '@grafana/data'; | ||||
| import { config } from '@grafana/runtime'; | ||||
| import { useStyles2, Spinner, Button } from '@grafana/ui'; | ||||
| import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA'; | ||||
| import { TermCount } from 'app/core/components/TagFilter/TagFilter'; | ||||
| import { FolderDTO } from 'app/types'; | ||||
| 
 | ||||
|  | @ -13,6 +13,7 @@ import { PreviewsSystemRequirements } from '../../components/PreviewsSystemRequi | |||
| import { useSearchQuery } from '../../hooks/useSearchQuery'; | ||||
| import { getGrafanaSearcher, SearchQuery } from '../../service'; | ||||
| import { SearchLayout } from '../../types'; | ||||
| import { reportDashboardListViewed } from '../reporting'; | ||||
| import { newSearchSelection, updateSearchSelection } from '../selection'; | ||||
| 
 | ||||
| import { ActionRow, getValidQueryLayout } from './ActionRow'; | ||||
|  | @ -80,6 +81,21 @@ export const SearchView = ({ | |||
|     return q; | ||||
|   }, [query, queryText, folderDTO, includePanels]); | ||||
| 
 | ||||
|   // Search usage reporting
 | ||||
|   useDebounce( | ||||
|     () => { | ||||
|       reportDashboardListViewed(folderDTO ? 'manage_dashboards' : 'dashboard_search', { | ||||
|         layout: query.layout, | ||||
|         starred: query.starred, | ||||
|         sortValue: query.sort?.value, | ||||
|         query: query.query, | ||||
|         tagCount: query.tag?.length, | ||||
|       }); | ||||
|     }, | ||||
|     1000, | ||||
|     [folderDTO, query.layout, query.starred, query.sort?.value, query.query?.length, query.tag?.length] | ||||
|   ); | ||||
| 
 | ||||
|   const results = useAsync(() => { | ||||
|     return getGrafanaSearcher().search(searchQuery); | ||||
|   }, [searchQuery]); | ||||
|  | @ -97,10 +113,6 @@ export const SearchView = ({ | |||
|     [searchSelection] | ||||
|   ); | ||||
| 
 | ||||
|   if (!config.featureToggles.panelTitleSearch) { | ||||
|     return <div className={styles.unsupported}>Unsupported</div>; | ||||
|   } | ||||
| 
 | ||||
|   // This gets the possible tags from within the query results
 | ||||
|   const getTagOptions = (): Promise<TermCount[]> => { | ||||
|     return getGrafanaSearcher().tags(searchQuery); | ||||
|  | @ -197,8 +209,19 @@ export const SearchView = ({ | |||
|     ); | ||||
|   }; | ||||
| 
 | ||||
|   if (!config.featureToggles.panelTitleSearch) { | ||||
|     return <div className={styles.unsupported}>Unsupported</div>; | ||||
|   if (folderDTO && !results.loading && !results.value?.totalRows && !queryText.length) { | ||||
|     return ( | ||||
|       <EmptyListCTA | ||||
|         title="This folder doesn't have any dashboards yet" | ||||
|         buttonIcon="plus" | ||||
|         buttonTitle="Create Dashboard" | ||||
|         buttonLink={`dashboard/new?folderId=${folderDTO.id}`} | ||||
|         proTip="Add/move dashboards to your folder at ->" | ||||
|         proTipLink="dashboards" | ||||
|         proTipLinkTitle="Manage dashboards" | ||||
|         proTipTarget="" | ||||
|       /> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   return ( | ||||
|  |  | |||
|  | @ -0,0 +1,26 @@ | |||
| import { config, reportInteraction } from '@grafana/runtime'; | ||||
| 
 | ||||
| import { SearchLayout } from '../types'; | ||||
| 
 | ||||
| export const reportDashboardListViewed = ( | ||||
|   dashboardListType: 'manage_dashboards' | 'dashboard_search', | ||||
|   query: { | ||||
|     layout?: SearchLayout; | ||||
|     starred?: boolean; | ||||
|     sortValue?: string; | ||||
|     query?: string; | ||||
|     tagCount?: number; | ||||
|   } | ||||
| ) => { | ||||
|   const showPreviews = query.layout === SearchLayout.Grid; | ||||
|   const previewsEnabled = Boolean(config.featureToggles.panelTitleSearch); | ||||
|   const previews = previewsEnabled ? (showPreviews ? 'on' : 'off') : 'feature_disabled'; | ||||
|   reportInteraction(`${dashboardListType}_viewed`, { | ||||
|     previews, | ||||
|     layout: query.layout, | ||||
|     starredFilter: query.starred ?? false, | ||||
|     sort: query.sortValue ?? '', | ||||
|     tagCount: query.tagCount ?? 0, | ||||
|     queryLength: query.query?.length ?? 0, | ||||
|   }); | ||||
| }; | ||||
|  | @ -6,6 +6,8 @@ import { TermCount } from 'app/core/components/TagFilter/TagFilter'; | |||
| import { GrafanaDatasource } from 'app/plugins/datasource/grafana/datasource'; | ||||
| import { GrafanaQueryType } from 'app/plugins/datasource/grafana/types'; | ||||
| 
 | ||||
| import { replaceCurrentFolderQuery } from './utils'; | ||||
| 
 | ||||
| import { DashboardQueryResult, GrafanaSearcher, QueryResponse, SearchQuery, SearchResultMeta } from '.'; | ||||
| 
 | ||||
| export class BlugeSearcher implements GrafanaSearcher { | ||||
|  | @ -65,6 +67,7 @@ const firstPageSize = 50; | |||
| const nextPageSizes = 100; | ||||
| 
 | ||||
| async function doSearchQuery(query: SearchQuery): Promise<QueryResponse> { | ||||
|   query = await replaceCurrentFolderQuery(query); | ||||
|   const ds = (await getDataSourceSrv().get('-- Grafana --')) as GrafanaDatasource; | ||||
|   const target = { | ||||
|     ...query, | ||||
|  |  | |||
|  | @ -6,6 +6,7 @@ import { backendSrv } from 'app/core/services/backend_srv'; | |||
| import { DashboardSearchHit } from '../types'; | ||||
| 
 | ||||
| import { LocationInfo } from './types'; | ||||
| import { replaceCurrentFolderQuery } from './utils'; | ||||
| 
 | ||||
| import { DashboardQueryResult, GrafanaSearcher, QueryResponse, SearchQuery } from '.'; | ||||
| 
 | ||||
|  | @ -46,6 +47,7 @@ export class SQLSearcher implements GrafanaSearcher { | |||
|       sort: query.sort, | ||||
|     }; | ||||
| 
 | ||||
|     query = await replaceCurrentFolderQuery(query); | ||||
|     if (query.query === '*') { | ||||
|       if (query.kind?.length === 1 && query.kind[0] === 'folder') { | ||||
|         q.type = 'dash-folder'; | ||||
|  |  | |||
|  | @ -0,0 +1,36 @@ | |||
| import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv'; | ||||
| 
 | ||||
| import { SearchQuery } from './types'; | ||||
| 
 | ||||
| /** prepare the query replacing folder:current */ | ||||
| export async function replaceCurrentFolderQuery(query: SearchQuery): Promise<SearchQuery> { | ||||
|   if (query.query && query.query.indexOf('folder:current') >= 0) { | ||||
|     query = { | ||||
|       ...query, | ||||
|       location: await getCurrentFolderUID(), | ||||
|       query: query.query.replace('folder:current', '').trim(), | ||||
|     }; | ||||
|     if (!query.query?.length) { | ||||
|       query.query = '*'; | ||||
|     } | ||||
|   } | ||||
|   return Promise.resolve(query); | ||||
| } | ||||
| 
 | ||||
| async function getCurrentFolderUID(): Promise<string | undefined> { | ||||
|   try { | ||||
|     let dash = getDashboardSrv().getCurrent(); | ||||
|     if (!dash) { | ||||
|       await delay(500); // may not be loaded yet
 | ||||
|       dash = getDashboardSrv().getCurrent(); | ||||
|     } | ||||
|     return Promise.resolve(dash?.meta?.folderUid); | ||||
|   } catch (e) { | ||||
|     console.error(e); | ||||
|   } | ||||
|   return undefined; | ||||
| } | ||||
| 
 | ||||
| function delay(ms: number) { | ||||
|   return new Promise((resolve) => setTimeout(resolve, ms)); | ||||
| } | ||||
		Loading…
	
		Reference in New Issue