Provisioning: Add dashboard preview banner (#102305)

* Provisioned: Add dashboard preview banner

* Add translations

* Add banner to the dashboard page

* Add translations

* Add feature toggle
This commit is contained in:
Alex Khomenko
2025-03-18 08:55:25 +02:00
committed by GitHub
parent 967425ab1b
commit 3454030bc2
4 changed files with 133 additions and 1 deletions
@@ -14,6 +14,7 @@ import { DashboardPageRouteParams, DashboardPageRouteSearchParams } from 'app/fe
import { DashboardRoutes } from 'app/types';
import { DashboardPrompt } from '../saving/DashboardPrompt';
import { DashboardPreviewBanner } from '../saving/provisioned/DashboardPreviewBanner';
import { getDashboardScenePageStateManager } from './DashboardScenePageStateManager';
@@ -76,6 +77,7 @@ export function DashboardScenePage({ route, queryParams, location }: Props) {
return (
<UrlSyncContextProvider scene={dashboard} updateUrlOnInit={true} createBrowserHistorySteps={true}>
<DashboardPreviewBanner queryParams={queryParams} route={route.routeName} slug={slug} path={path} />
<dashboard.Component model={dashboard} key={dashboard.state.key} />
<DashboardPrompt dashboard={dashboard} />
</UrlSyncContextProvider>
@@ -61,8 +61,8 @@ interface DashboardCacheEntry<T> {
export interface LoadDashboardOptions {
uid: string;
route: DashboardRoutes;
type?: string;
slug?: string;
type?: string;
urlFolderUid?: string;
params?: {
version: number;
@@ -0,0 +1,119 @@
import { config } from '@grafana/runtime';
import { Alert, Icon, Stack } from '@grafana/ui';
import { useGetRepositoryFilesWithPathQuery } from 'app/api/clients/provisioning';
import { t, Trans } from 'app/core/internationalization';
import { DashboardPageRouteSearchParams } from 'app/features/dashboard/containers/types';
import { usePullRequestParam } from 'app/features/provisioning/hooks/usePullRequestParam';
import { DashboardRoutes } from 'app/types';
interface CommonBannerProps {
queryParams: DashboardPageRouteSearchParams;
path?: string;
slug?: string;
}
interface DashboardPreviewBannerProps extends CommonBannerProps {
route?: string;
}
interface DashboardPreviewBannerContentProps extends Required<Omit<CommonBannerProps, 'route'>> {}
const commonAlertProps = {
severity: 'info' as const,
style: { flex: 0 } as const,
};
function DashboardPreviewBannerContent({ queryParams, slug, path }: DashboardPreviewBannerContentProps) {
const prParam = usePullRequestParam();
const file = useGetRepositoryFilesWithPathQuery({ name: slug, path, ref: queryParams.ref });
if (file.data?.errors) {
return (
<Alert
title={t('dashboard-scene.dashboard-preview-banner.title-error-loading-dashboard', 'Error loading dashboard')}
severity="error"
style={{ flex: 0 }}
>
{file.data.errors.map((error, index) => (
<div key={index}>{error}</div>
))}
</Alert>
);
}
// This page was loaded with a `pull_request_url` in the URL
if (prParam?.length) {
return (
<Alert
{...commonAlertProps}
title={t(
'dashboard-scene.dashboard-preview-banner.title-dashboard-loaded-request-git-hub',
'This dashboard is loaded from a pull request in GitHub.'
)}
buttonContent={
<Stack alignItems="center">
<Trans i18nKey="dashboard-scene.dashboard-preview-banner.view-pull-request-in-git-hub">
View pull request in GitHub
</Trans>
<Icon name="external-link-alt" />
</Stack>
}
onRemove={() => window.open(prParam, '_blank')}
>
<Trans i18nKey="dashboard-scene.dashboard-preview-banner.value-not-saved">
The value is not yet saved in the Grafana database
</Trans>
</Alert>
);
}
// Check if this is a GitHub link
const githubURL = file.data?.urls?.newPullRequestURL ?? file.data?.urls?.compareURL;
if (githubURL) {
return (
<Alert
{...commonAlertProps}
title={t(
'dashboard-scene.dashboard-preview-banner.title-dashboard-loaded-branch-git-hub',
'This dashboard is loaded from a branch in GitHub.'
)}
buttonContent={
<Stack alignItems="center">
<Trans i18nKey="dashboard-scene.dashboard-preview-banner.open-pull-request-in-git-hub">
Open pull request in GitHub
</Trans>
<Icon name="external-link-alt" />
</Stack>
}
onRemove={() => window.open(githubURL, '_blank')}
>
<Trans i18nKey="dashboard-scene.dashboard-preview-banner.not-saved">
The value is not yet saved in the Grafana database
</Trans>
</Alert>
);
}
return (
<Alert
{...commonAlertProps}
title={t(
'dashboard-scene.dashboard-preview-banner.title-dashboard-loaded-external-repository',
'This dashboard is loaded from an external repository'
)}
>
<Trans i18nKey="dashboard-scene.dashboard-preview-banner.not-yet-saved">
The value is not saved in the Grafana database
</Trans>
</Alert>
);
}
export function DashboardPreviewBanner({ queryParams, route, slug, path }: DashboardPreviewBannerProps) {
const provisioningEnabled = config.featureToggles.provisioning;
if (!provisioningEnabled || 'kiosk' in queryParams || !path || route !== DashboardRoutes.Provisioning || !slug) {
return null;
}
return <DashboardPreviewBannerContent queryParams={queryParams} slug={slug} path={path} />;
}
+11
View File
@@ -1438,6 +1438,17 @@
"it-cannot-contain-or": "It cannot contain '//' or '..'.",
"least-valid-character": "It must have at least one valid character."
},
"dashboard-preview-banner": {
"not-saved": "The value is not yet saved in the Grafana database",
"not-yet-saved": "The value is not saved in the Grafana database",
"open-pull-request-in-git-hub": "Open pull request in GitHub",
"title-dashboard-loaded-branch-git-hub": "This dashboard is loaded from a branch in GitHub.",
"title-dashboard-loaded-external-repository": "This dashboard is loaded from an external repository",
"title-dashboard-loaded-request-git-hub": "This dashboard is loaded from a pull request in GitHub.",
"title-error-loading-dashboard": "Error loading dashboard",
"value-not-saved": "The value is not yet saved in the Grafana database",
"view-pull-request-in-git-hub": "View pull request in GitHub"
},
"provisioned-resource-delete-modal": {
"file-path": "File path:",
"managed-by-version-control": "This {type} is managed by version control and cannot be deleted. To remove it, delete it from the repository and synchronise to apply the changes.",