add save query library functionality stub
This commit is contained in:
@@ -8,72 +8,80 @@ import { ExpressionQueryType } from 'app/features/expressions/types';
|
||||
|
||||
interface AddDataItemMenuProps {
|
||||
onAddQuery: () => void;
|
||||
onAddFromSavedQueries: () => void;
|
||||
onAddTransform: () => void;
|
||||
onAddExpression: (type: ExpressionQueryType) => void;
|
||||
}
|
||||
|
||||
export const AddDataItemMenu = memo(({ onAddQuery, onAddTransform, onAddExpression }: AddDataItemMenuProps) => {
|
||||
const expressionTypes = [
|
||||
{ type: ExpressionQueryType.math, label: t('dashboard-scene.add-data-item-menu.expression-math', 'Math') },
|
||||
{ type: ExpressionQueryType.reduce, label: t('dashboard-scene.add-data-item-menu.expression-reduce', 'Reduce') },
|
||||
{
|
||||
type: ExpressionQueryType.resample,
|
||||
label: t('dashboard-scene.add-data-item-menu.expression-resample', 'Resample'),
|
||||
},
|
||||
{
|
||||
type: ExpressionQueryType.classic,
|
||||
label: t('dashboard-scene.add-data-item-menu.expression-classic', 'Classic condition'),
|
||||
},
|
||||
{
|
||||
type: ExpressionQueryType.threshold,
|
||||
label: t('dashboard-scene.add-data-item-menu.expression-threshold', 'Threshold'),
|
||||
},
|
||||
];
|
||||
export const AddDataItemMenu = memo(
|
||||
({ onAddQuery, onAddFromSavedQueries, onAddTransform, onAddExpression }: AddDataItemMenuProps) => {
|
||||
const expressionTypes = [
|
||||
{ type: ExpressionQueryType.math, label: t('dashboard-scene.add-data-item-menu.expression-math', 'Math') },
|
||||
{ type: ExpressionQueryType.reduce, label: t('dashboard-scene.add-data-item-menu.expression-reduce', 'Reduce') },
|
||||
{
|
||||
type: ExpressionQueryType.resample,
|
||||
label: t('dashboard-scene.add-data-item-menu.expression-resample', 'Resample'),
|
||||
},
|
||||
{
|
||||
type: ExpressionQueryType.classic,
|
||||
label: t('dashboard-scene.add-data-item-menu.expression-classic', 'Classic condition'),
|
||||
},
|
||||
{
|
||||
type: ExpressionQueryType.threshold,
|
||||
label: t('dashboard-scene.add-data-item-menu.expression-threshold', 'Threshold'),
|
||||
},
|
||||
];
|
||||
|
||||
// Add SQL if feature flag is enabled
|
||||
if (config.featureToggles.sqlExpressions) {
|
||||
expressionTypes.push({
|
||||
type: ExpressionQueryType.sql,
|
||||
label: t('dashboard-scene.add-data-item-menu.expression-sql', 'SQL'),
|
||||
});
|
||||
}
|
||||
// Add SQL if feature flag is enabled
|
||||
if (config.featureToggles.sqlExpressions) {
|
||||
expressionTypes.push({
|
||||
type: ExpressionQueryType.sql,
|
||||
label: t('dashboard-scene.add-data-item-menu.expression-sql', 'SQL'),
|
||||
});
|
||||
}
|
||||
|
||||
const expressionSubItems = expressionTypes.map(({ type, label }) => (
|
||||
<Menu.Item key={type} label={label} icon={EXPRESSION_ICON_MAP[type]} onClick={() => onAddExpression(type)} />
|
||||
));
|
||||
const expressionSubItems = expressionTypes.map(({ type, label }) => (
|
||||
<Menu.Item key={type} label={label} icon={EXPRESSION_ICON_MAP[type]} onClick={() => onAddExpression(type)} />
|
||||
));
|
||||
|
||||
const menu = (
|
||||
<Menu>
|
||||
<Menu.Item
|
||||
label={t('dashboard-scene.add-data-item-menu.add-query', 'Query')}
|
||||
icon="database"
|
||||
onClick={onAddQuery}
|
||||
/>
|
||||
<Menu.Item
|
||||
label={t('dashboard-scene.add-data-item-menu.add-transformation', 'Transformation')}
|
||||
icon="process"
|
||||
onClick={onAddTransform}
|
||||
/>
|
||||
<Menu.Item
|
||||
label={t('dashboard-scene.add-data-item-menu.expressions-group', 'Expression')}
|
||||
icon="calculator-alt"
|
||||
childItems={expressionSubItems}
|
||||
/>
|
||||
</Menu>
|
||||
);
|
||||
|
||||
return (
|
||||
<Stack direction="row" justifyContent="center" alignItems="center">
|
||||
<Dropdown overlay={menu} placement="top-start">
|
||||
<IconButton
|
||||
name="plus-circle"
|
||||
size="xxl"
|
||||
variant="secondary"
|
||||
tooltip={t('dashboard-scene.add-data-item-menu.add-button', 'Add')}
|
||||
const menu = (
|
||||
<Menu>
|
||||
<Menu.Item
|
||||
label={t('dashboard-scene.add-data-item-menu.add-query', 'Query')}
|
||||
icon="database"
|
||||
onClick={onAddQuery}
|
||||
/>
|
||||
</Dropdown>
|
||||
</Stack>
|
||||
);
|
||||
});
|
||||
<Menu.Item
|
||||
label={t('dashboard-scene.add-data-item-menu.add-from-saved-queries', 'From saved queries')}
|
||||
icon="bookmark"
|
||||
onClick={onAddFromSavedQueries}
|
||||
/>
|
||||
<Menu.Item
|
||||
label={t('dashboard-scene.add-data-item-menu.add-transformation', 'Transformation')}
|
||||
icon="process"
|
||||
onClick={onAddTransform}
|
||||
/>
|
||||
<Menu.Item
|
||||
label={t('dashboard-scene.add-data-item-menu.expressions-group', 'Expression')}
|
||||
icon="calculator-alt"
|
||||
childItems={expressionSubItems}
|
||||
/>
|
||||
</Menu>
|
||||
);
|
||||
|
||||
return (
|
||||
<Stack direction="row" justifyContent="center" alignItems="center">
|
||||
<Dropdown overlay={menu} placement="top-start">
|
||||
<IconButton
|
||||
name="plus-circle"
|
||||
size="xxl"
|
||||
variant="secondary"
|
||||
tooltip={t('dashboard-scene.add-data-item-menu.add-button', 'Add')}
|
||||
/>
|
||||
</Dropdown>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
AddDataItemMenu.displayName = 'AddDataItemMenu';
|
||||
|
||||
@@ -28,6 +28,7 @@ import { DataSourcePicker } from 'app/features/datasources/components/picker/Dat
|
||||
import { getQueryRunnerFor } from '../../utils/utils';
|
||||
|
||||
import { QueryTransformItem } from './QueryTransformList';
|
||||
import { SavedQueriesDrawer } from './SavedQueriesDrawer';
|
||||
|
||||
interface DetailViewHeaderProps {
|
||||
selectedItem: QueryTransformItem;
|
||||
@@ -63,6 +64,7 @@ export const DetailViewHeader = ({
|
||||
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const [validationError, setValidationError] = useState<string | null>(null);
|
||||
const [isSavedQueriesDrawerOpen, setIsSavedQueriesDrawerOpen] = useState(false);
|
||||
|
||||
// Helper to update queries with consistent pattern
|
||||
const updateQueries = useCallback(
|
||||
@@ -265,6 +267,23 @@ export const DetailViewHeader = ({
|
||||
onToggleTransformVisibility?.(selectedItem.index);
|
||||
}, [selectedItem, onToggleTransformVisibility]);
|
||||
|
||||
// Handler for selecting a query from the library (replaces current query)
|
||||
const onSelectQueryFromLibrary = useCallback(
|
||||
(newQuery: DataQuery) => {
|
||||
if (selectedItem.type !== 'query' || selectedItem.index === undefined) {
|
||||
return;
|
||||
}
|
||||
updateQueries(
|
||||
(queries) =>
|
||||
queries.map((q, idx) =>
|
||||
idx === selectedItem.index ? { ...newQuery, refId: q.refId, datasource: q.datasource } : q
|
||||
),
|
||||
true
|
||||
);
|
||||
},
|
||||
[selectedItem, updateQueries]
|
||||
);
|
||||
|
||||
const refId = 'refId' in selectedItem.data ? selectedItem.data.refId : '';
|
||||
const isHidden =
|
||||
(selectedItem.type === 'query' || selectedItem.type === 'expression') &&
|
||||
@@ -342,6 +361,19 @@ export const DetailViewHeader = ({
|
||||
{/* Right side: Run Query + Actions Menu for queries/expressions */}
|
||||
{(selectedItem.type === 'query' || selectedItem.type === 'expression') && (
|
||||
<Stack gap={0.5} alignItems="center">
|
||||
{/* Save Query Button (only for queries, not expressions) */}
|
||||
{selectedItem.type === 'query' && 'refId' in selectedItem.data && (
|
||||
<Button
|
||||
className={styles.monospace}
|
||||
variant="primary"
|
||||
fill="text"
|
||||
size="sm"
|
||||
icon="bookmark"
|
||||
onClick={() => setIsSavedQueriesDrawerOpen(true)}
|
||||
>
|
||||
{t('dashboard-scene.detail-view-header.save-query', 'SAVE')}
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
className={styles.monospace}
|
||||
variant="primary"
|
||||
@@ -420,6 +452,25 @@ export const DetailViewHeader = ({
|
||||
</Stack>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Saved Queries Drawer */}
|
||||
{selectedItem.type === 'query' && 'refId' in selectedItem.data && (
|
||||
<SavedQueriesDrawer
|
||||
isOpen={isSavedQueriesDrawerOpen}
|
||||
onClose={() => setIsSavedQueriesDrawerOpen(false)}
|
||||
onSelectQuery={onSelectQueryFromLibrary}
|
||||
currentQuery={
|
||||
'datasource' in selectedItem.data
|
||||
? {
|
||||
refId: selectedItem.data.refId,
|
||||
datasource: datasourceSettings
|
||||
? { uid: datasourceSettings.uid, type: datasourceSettings.type }
|
||||
: selectedItem.data.datasource,
|
||||
}
|
||||
: { refId: selectedItem.data.refId }
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { css, cx } from '@emotion/css';
|
||||
import { useCallback, useMemo, useState, useEffect, useRef } from 'react';
|
||||
|
||||
import { DataTransformerConfig, GrafanaTheme2, SelectableValue } from '@grafana/data';
|
||||
import { DataQuery, DataTransformerConfig, GrafanaTheme2, SelectableValue } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import {
|
||||
SceneComponentProps,
|
||||
@@ -30,6 +30,7 @@ import { PanelDataAlertingTab } from './PanelDataAlertingTab';
|
||||
import { PanelDataQueriesTab } from './PanelDataQueriesTab';
|
||||
import { PanelDataTransformationsTab } from './PanelDataTransformationsTab';
|
||||
import { QueryTransformList, QueryTransformItem } from './QueryTransformList';
|
||||
import { SavedQueriesDrawer } from './SavedQueriesDrawer';
|
||||
import { TransformationsDrawer } from './TransformationsDrawer';
|
||||
import { PanelDataPaneTab, TabId } from './types';
|
||||
import { isDataTransformerConfig, queryItemId, transformItemId } from './utils';
|
||||
@@ -89,6 +90,7 @@ function PanelDataPaneRendered({ model }: SceneComponentProps<PanelDataPane>) {
|
||||
const styles = useStyles2(getStyles);
|
||||
const [selectedId, setSelectedId] = useState<string | null>(null);
|
||||
const [transformDrawerOpen, setTransformDrawerOpen] = useState(false);
|
||||
const [savedQueriesDrawerOpen, setSavedQueriesDrawerOpen] = useState(false);
|
||||
|
||||
const panel = panelRef.resolve();
|
||||
|
||||
@@ -205,6 +207,49 @@ function PanelDataPaneRendered({ model }: SceneComponentProps<PanelDataPane>) {
|
||||
}
|
||||
}, [tabs, panel]);
|
||||
|
||||
const handleAddFromSavedQueries = useCallback(() => {
|
||||
setSavedQueriesDrawerOpen(true);
|
||||
}, []);
|
||||
|
||||
// This is a stub for the saved queries drawer
|
||||
const handleSelectSavedQuery = useCallback(
|
||||
(query: DataQuery) => {
|
||||
if (!queryRunner) {
|
||||
return;
|
||||
}
|
||||
|
||||
const queries = queryRunner.state.queries || [];
|
||||
|
||||
// Get next available refId
|
||||
const existingRefIds = queries.map((q) => q.refId);
|
||||
let nextRefId = 'A';
|
||||
const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
|
||||
for (let i = 0; i < alphabet.length; i++) {
|
||||
if (!existingRefIds.includes(alphabet[i])) {
|
||||
nextRefId = alphabet[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Create new query with the selected refId
|
||||
const newQuery: DataQuery = {
|
||||
...query,
|
||||
refId: nextRefId,
|
||||
};
|
||||
|
||||
queryRunner.setState({ queries: [...queries, newQuery] });
|
||||
|
||||
// Select the new query after a short delay
|
||||
setTimeout(() => {
|
||||
setSelectedId(queryItemId(newQuery));
|
||||
}, 100);
|
||||
|
||||
setSavedQueriesDrawerOpen(false);
|
||||
},
|
||||
[queryRunner]
|
||||
);
|
||||
|
||||
const handleAddTransform = useCallback(() => {
|
||||
setTransformDrawerOpen(true);
|
||||
}, []);
|
||||
@@ -480,6 +525,7 @@ function PanelDataPaneRendered({ model }: SceneComponentProps<PanelDataPane>) {
|
||||
selectedId={effectiveSelectedId}
|
||||
onSelect={handleSelect}
|
||||
onAddQuery={handleAddQuery}
|
||||
onAddFromSavedQueries={handleAddFromSavedQueries}
|
||||
onAddTransform={handleAddTransform}
|
||||
onAddExpression={handleAddExpression}
|
||||
onDuplicateQuery={handleDuplicateQuery}
|
||||
@@ -516,6 +562,11 @@ function PanelDataPaneRendered({ model }: SceneComponentProps<PanelDataPane>) {
|
||||
onTransformationAdd={handleTransformationAdd}
|
||||
series={series}
|
||||
/>
|
||||
<SavedQueriesDrawer
|
||||
isOpen={savedQueriesDrawerOpen}
|
||||
onClose={() => setSavedQueriesDrawerOpen(false)}
|
||||
onSelectQuery={handleSelectSavedQuery}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ interface QueryTransformListProps {
|
||||
selectedId: string | null;
|
||||
onSelect: (id: string) => void;
|
||||
onAddQuery: () => void;
|
||||
onAddFromSavedQueries: () => void;
|
||||
onAddTransform: () => void;
|
||||
onAddExpression: (type: ExpressionQueryType) => void;
|
||||
onDuplicateQuery?: (index: number) => void;
|
||||
@@ -48,6 +49,7 @@ export const QueryTransformList = memo(
|
||||
selectedId,
|
||||
onSelect,
|
||||
onAddQuery,
|
||||
onAddFromSavedQueries,
|
||||
onAddTransform,
|
||||
onAddExpression,
|
||||
onDuplicateQuery,
|
||||
@@ -294,6 +296,7 @@ export const QueryTransformList = memo(
|
||||
|
||||
<AddDataItemMenu
|
||||
onAddQuery={onAddQuery}
|
||||
onAddFromSavedQueries={onAddFromSavedQueries}
|
||||
onAddTransform={onAddTransform}
|
||||
onAddExpression={onAddExpression}
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,291 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { DataQuery, GrafanaTheme2 } from '@grafana/data';
|
||||
import { Trans, t } from '@grafana/i18n';
|
||||
import { Button, Drawer, Icon, Input, Stack, useStyles2 } from '@grafana/ui';
|
||||
|
||||
interface SavedQueriesDrawerProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onSelectQuery: (query: DataQuery) => void;
|
||||
currentQuery?: DataQuery;
|
||||
}
|
||||
|
||||
// Stub saved queries for demonstration
|
||||
const STUB_SAVED_QUERIES: Array<{ id: string; name: string; description: string; query: DataQuery }> = [
|
||||
{
|
||||
id: '1',
|
||||
name: 'CPU Usage Query',
|
||||
description: 'Returns CPU usage metrics for all hosts',
|
||||
query: {
|
||||
refId: 'A',
|
||||
datasource: { type: 'prometheus', uid: 'prometheus' },
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
name: 'Memory Usage Query',
|
||||
description: 'Returns memory usage metrics',
|
||||
query: {
|
||||
refId: 'A',
|
||||
datasource: { type: 'prometheus', uid: 'prometheus' },
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
name: 'Network Traffic Query',
|
||||
description: 'Returns network traffic in/out bytes',
|
||||
query: {
|
||||
refId: 'A',
|
||||
datasource: { type: 'prometheus', uid: 'prometheus' },
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
name: 'Disk I/O Query',
|
||||
description: 'Returns disk read/write operations',
|
||||
query: {
|
||||
refId: 'A',
|
||||
datasource: { type: 'prometheus', uid: 'prometheus' },
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export function SavedQueriesDrawer({ isOpen, onClose, onSelectQuery, currentQuery }: SavedQueriesDrawerProps) {
|
||||
const styles = useStyles2(getStyles);
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [isSaveMode, setIsSaveMode] = useState(false);
|
||||
const [saveName, setSaveName] = useState('');
|
||||
const [saveDescription, setSaveDescription] = useState('');
|
||||
|
||||
const filteredQueries = STUB_SAVED_QUERIES.filter(
|
||||
(q) =>
|
||||
q.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
q.description.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
);
|
||||
|
||||
const handleSelectQuery = (query: DataQuery) => {
|
||||
onSelectQuery(query);
|
||||
onClose();
|
||||
};
|
||||
|
||||
const handleSaveQuery = () => {
|
||||
// Stub: In real implementation, this would save to the query library
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Saving query:', { name: saveName, description: saveDescription, query: currentQuery });
|
||||
alert(t('dashboard-scene.saved-queries-drawer.save-success', 'Query saved successfully! (stub)'));
|
||||
onClose();
|
||||
};
|
||||
|
||||
if (!isOpen) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
title={
|
||||
isSaveMode
|
||||
? t('dashboard-scene.saved-queries-drawer.save-title', 'Save Query')
|
||||
: t('dashboard-scene.saved-queries-drawer.title', 'Saved Queries')
|
||||
}
|
||||
onClose={onClose}
|
||||
size="md"
|
||||
>
|
||||
<div className={styles.container}>
|
||||
{/* Tab-like buttons to switch modes */}
|
||||
<Stack gap={1} direction="row">
|
||||
<Button
|
||||
variant={!isSaveMode ? 'primary' : 'secondary'}
|
||||
fill={!isSaveMode ? 'solid' : 'outline'}
|
||||
size="sm"
|
||||
onClick={() => setIsSaveMode(false)}
|
||||
>
|
||||
<Trans i18nKey="dashboard-scene.saved-queries-drawer.browse-tab">Browse</Trans>
|
||||
</Button>
|
||||
<Button
|
||||
variant={isSaveMode ? 'primary' : 'secondary'}
|
||||
fill={isSaveMode ? 'solid' : 'outline'}
|
||||
size="sm"
|
||||
onClick={() => setIsSaveMode(true)}
|
||||
>
|
||||
<Trans i18nKey="dashboard-scene.saved-queries-drawer.save-tab">Save Current</Trans>
|
||||
</Button>
|
||||
</Stack>
|
||||
|
||||
{isSaveMode ? (
|
||||
/* Save Mode */
|
||||
<div className={styles.saveForm}>
|
||||
<Stack direction="column" gap={2}>
|
||||
<div>
|
||||
<label htmlFor="saved-query-name" className={styles.label}>
|
||||
<Trans i18nKey="dashboard-scene.saved-queries-drawer.name-label">Name</Trans>
|
||||
</label>
|
||||
<Input
|
||||
id="saved-query-name"
|
||||
value={saveName}
|
||||
onChange={(e) => setSaveName(e.currentTarget.value)}
|
||||
placeholder={t('dashboard-scene.saved-queries-drawer.name-placeholder', 'Enter query name...')}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="saved-query-description" className={styles.label}>
|
||||
<Trans i18nKey="dashboard-scene.saved-queries-drawer.description-label">Description</Trans>
|
||||
</label>
|
||||
<Input
|
||||
id="saved-query-description"
|
||||
value={saveDescription}
|
||||
onChange={(e) => setSaveDescription(e.currentTarget.value)}
|
||||
placeholder={t(
|
||||
'dashboard-scene.saved-queries-drawer.description-placeholder',
|
||||
'Enter description...'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.queryPreview}>
|
||||
<span className={styles.label}>
|
||||
<Trans i18nKey="dashboard-scene.saved-queries-drawer.query-preview">Query Preview</Trans>
|
||||
</span>
|
||||
<pre className={styles.queryCode}>{JSON.stringify(currentQuery, null, 2)}</pre>
|
||||
</div>
|
||||
<Button onClick={handleSaveQuery} disabled={!saveName.trim()}>
|
||||
<Trans i18nKey="dashboard-scene.saved-queries-drawer.save-button">Save Query</Trans>
|
||||
</Button>
|
||||
</Stack>
|
||||
</div>
|
||||
) : (
|
||||
/* Browse Mode */
|
||||
<>
|
||||
<div className={styles.search}>
|
||||
<Input
|
||||
prefix={<Icon name="search" />}
|
||||
placeholder={t('dashboard-scene.saved-queries-drawer.search-placeholder', 'Search saved queries...')}
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.currentTarget.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.queryList}>
|
||||
{filteredQueries.length === 0 ? (
|
||||
<div className={styles.emptyState}>
|
||||
<Trans i18nKey="dashboard-scene.saved-queries-drawer.no-results">No queries found</Trans>
|
||||
</div>
|
||||
) : (
|
||||
filteredQueries.map((savedQuery) => (
|
||||
<div key={savedQuery.id} className={styles.queryItem}>
|
||||
<div className={styles.queryInfo}>
|
||||
<div className={styles.queryName}>{savedQuery.name}</div>
|
||||
<div className={styles.queryDescription}>{savedQuery.description}</div>
|
||||
</div>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
onClick={() => handleSelectQuery(savedQuery.query)}
|
||||
icon="arrow-right"
|
||||
>
|
||||
<Trans i18nKey="dashboard-scene.saved-queries-drawer.use-query">Use</Trans>
|
||||
</Button>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className={styles.stubNotice}>
|
||||
<Icon name="info-circle" />
|
||||
<span>
|
||||
<Trans i18nKey="dashboard-scene.saved-queries-drawer.stub-notice">
|
||||
This is a stub implementation. Enable the queryLibrary feature toggle for full functionality.
|
||||
</Trans>
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</Drawer>
|
||||
);
|
||||
}
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => ({
|
||||
container: css({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: theme.spacing(2),
|
||||
height: '100%',
|
||||
}),
|
||||
search: css({
|
||||
marginTop: theme.spacing(1),
|
||||
}),
|
||||
queryList: css({
|
||||
flex: 1,
|
||||
overflow: 'auto',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: theme.spacing(1),
|
||||
}),
|
||||
queryItem: css({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
padding: theme.spacing(1.5),
|
||||
background: theme.colors.background.secondary,
|
||||
borderRadius: theme.shape.radius.default,
|
||||
border: `1px solid ${theme.colors.border.weak}`,
|
||||
'&:hover': {
|
||||
background: theme.colors.action.hover,
|
||||
borderColor: theme.colors.border.medium,
|
||||
},
|
||||
}),
|
||||
queryInfo: css({
|
||||
flex: 1,
|
||||
minWidth: 0,
|
||||
}),
|
||||
queryName: css({
|
||||
fontWeight: theme.typography.fontWeightMedium,
|
||||
color: theme.colors.text.primary,
|
||||
marginBottom: theme.spacing(0.5),
|
||||
}),
|
||||
queryDescription: css({
|
||||
fontSize: theme.typography.bodySmall.fontSize,
|
||||
color: theme.colors.text.secondary,
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
}),
|
||||
emptyState: css({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
padding: theme.spacing(4),
|
||||
color: theme.colors.text.secondary,
|
||||
}),
|
||||
stubNotice: css({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: theme.spacing(1),
|
||||
padding: theme.spacing(1),
|
||||
background: theme.colors.info.transparent,
|
||||
borderRadius: theme.shape.radius.default,
|
||||
color: theme.colors.info.text,
|
||||
fontSize: theme.typography.bodySmall.fontSize,
|
||||
}),
|
||||
saveForm: css({
|
||||
marginTop: theme.spacing(2),
|
||||
}),
|
||||
label: css({
|
||||
display: 'block',
|
||||
marginBottom: theme.spacing(0.5),
|
||||
fontWeight: theme.typography.fontWeightMedium,
|
||||
color: theme.colors.text.primary,
|
||||
}),
|
||||
queryPreview: css({
|
||||
marginTop: theme.spacing(1),
|
||||
}),
|
||||
queryCode: css({
|
||||
background: theme.colors.background.secondary,
|
||||
padding: theme.spacing(1),
|
||||
borderRadius: theme.shape.radius.default,
|
||||
fontSize: theme.typography.bodySmall.fontSize,
|
||||
overflow: 'auto',
|
||||
maxHeight: '200px',
|
||||
}),
|
||||
});
|
||||
Reference in New Issue
Block a user