mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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}
|
Datasource: {query.datasource}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{layout !== SearchLayout.Folders && (
|
{layout !== SearchLayout.Folders && config.featureToggles.panelTitleSearch && (
|
||||||
<Checkbox value={includePanels} onChange={() => setIncludePanels(!includePanels)} label="Include panels" />
|
<Checkbox value={includePanels} onChange={() => setIncludePanels(!includePanels)} label="Include panels" />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import React, { useCallback, useMemo, useState } from 'react';
|
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 AutoSizer from 'react-virtualized-auto-sizer';
|
||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data';
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
import { config } from '@grafana/runtime';
|
|
||||||
import { useStyles2, Spinner, Button } from '@grafana/ui';
|
import { useStyles2, Spinner, Button } from '@grafana/ui';
|
||||||
|
import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';
|
||||||
import { TermCount } from 'app/core/components/TagFilter/TagFilter';
|
import { TermCount } from 'app/core/components/TagFilter/TagFilter';
|
||||||
import { FolderDTO } from 'app/types';
|
import { FolderDTO } from 'app/types';
|
||||||
|
|
||||||
@ -13,6 +13,7 @@ import { PreviewsSystemRequirements } from '../../components/PreviewsSystemRequi
|
|||||||
import { useSearchQuery } from '../../hooks/useSearchQuery';
|
import { useSearchQuery } from '../../hooks/useSearchQuery';
|
||||||
import { getGrafanaSearcher, SearchQuery } from '../../service';
|
import { getGrafanaSearcher, SearchQuery } from '../../service';
|
||||||
import { SearchLayout } from '../../types';
|
import { SearchLayout } from '../../types';
|
||||||
|
import { reportDashboardListViewed } from '../reporting';
|
||||||
import { newSearchSelection, updateSearchSelection } from '../selection';
|
import { newSearchSelection, updateSearchSelection } from '../selection';
|
||||||
|
|
||||||
import { ActionRow, getValidQueryLayout } from './ActionRow';
|
import { ActionRow, getValidQueryLayout } from './ActionRow';
|
||||||
@ -80,6 +81,21 @@ export const SearchView = ({
|
|||||||
return q;
|
return q;
|
||||||
}, [query, queryText, folderDTO, includePanels]);
|
}, [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(() => {
|
const results = useAsync(() => {
|
||||||
return getGrafanaSearcher().search(searchQuery);
|
return getGrafanaSearcher().search(searchQuery);
|
||||||
}, [searchQuery]);
|
}, [searchQuery]);
|
||||||
@ -97,10 +113,6 @@ export const SearchView = ({
|
|||||||
[searchSelection]
|
[searchSelection]
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!config.featureToggles.panelTitleSearch) {
|
|
||||||
return <div className={styles.unsupported}>Unsupported</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This gets the possible tags from within the query results
|
// This gets the possible tags from within the query results
|
||||||
const getTagOptions = (): Promise<TermCount[]> => {
|
const getTagOptions = (): Promise<TermCount[]> => {
|
||||||
return getGrafanaSearcher().tags(searchQuery);
|
return getGrafanaSearcher().tags(searchQuery);
|
||||||
@ -197,8 +209,19 @@ export const SearchView = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!config.featureToggles.panelTitleSearch) {
|
if (folderDTO && !results.loading && !results.value?.totalRows && !queryText.length) {
|
||||||
return <div className={styles.unsupported}>Unsupported</div>;
|
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 (
|
return (
|
||||||
|
26
public/app/features/search/page/reporting.ts
Normal file
26
public/app/features/search/page/reporting.ts
Normal file
@ -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 { GrafanaDatasource } from 'app/plugins/datasource/grafana/datasource';
|
||||||
import { GrafanaQueryType } from 'app/plugins/datasource/grafana/types';
|
import { GrafanaQueryType } from 'app/plugins/datasource/grafana/types';
|
||||||
|
|
||||||
|
import { replaceCurrentFolderQuery } from './utils';
|
||||||
|
|
||||||
import { DashboardQueryResult, GrafanaSearcher, QueryResponse, SearchQuery, SearchResultMeta } from '.';
|
import { DashboardQueryResult, GrafanaSearcher, QueryResponse, SearchQuery, SearchResultMeta } from '.';
|
||||||
|
|
||||||
export class BlugeSearcher implements GrafanaSearcher {
|
export class BlugeSearcher implements GrafanaSearcher {
|
||||||
@ -65,6 +67,7 @@ const firstPageSize = 50;
|
|||||||
const nextPageSizes = 100;
|
const nextPageSizes = 100;
|
||||||
|
|
||||||
async function doSearchQuery(query: SearchQuery): Promise<QueryResponse> {
|
async function doSearchQuery(query: SearchQuery): Promise<QueryResponse> {
|
||||||
|
query = await replaceCurrentFolderQuery(query);
|
||||||
const ds = (await getDataSourceSrv().get('-- Grafana --')) as GrafanaDatasource;
|
const ds = (await getDataSourceSrv().get('-- Grafana --')) as GrafanaDatasource;
|
||||||
const target = {
|
const target = {
|
||||||
...query,
|
...query,
|
||||||
|
@ -6,6 +6,7 @@ import { backendSrv } from 'app/core/services/backend_srv';
|
|||||||
import { DashboardSearchHit } from '../types';
|
import { DashboardSearchHit } from '../types';
|
||||||
|
|
||||||
import { LocationInfo } from './types';
|
import { LocationInfo } from './types';
|
||||||
|
import { replaceCurrentFolderQuery } from './utils';
|
||||||
|
|
||||||
import { DashboardQueryResult, GrafanaSearcher, QueryResponse, SearchQuery } from '.';
|
import { DashboardQueryResult, GrafanaSearcher, QueryResponse, SearchQuery } from '.';
|
||||||
|
|
||||||
@ -46,6 +47,7 @@ export class SQLSearcher implements GrafanaSearcher {
|
|||||||
sort: query.sort,
|
sort: query.sort,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
query = await replaceCurrentFolderQuery(query);
|
||||||
if (query.query === '*') {
|
if (query.query === '*') {
|
||||||
if (query.kind?.length === 1 && query.kind[0] === 'folder') {
|
if (query.kind?.length === 1 && query.kind[0] === 'folder') {
|
||||||
q.type = 'dash-folder';
|
q.type = 'dash-folder';
|
||||||
|
36
public/app/features/search/service/utils.ts
Normal file
36
public/app/features/search/service/utils.ts
Normal file
@ -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
Block a user