update preview banner

This commit is contained in:
Ryan McKinley committed 2025-02-23 22:36:51 +03:00
1 parent 57c46ffe71
commit 122cfb568b
8 files changed
+128 -85

No files matched your search

+17 -8
View File
@@ -3364,10 +3364,21 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "5"]
],
"public/app/features/dashboard-scene/saving/provisioned/DashboardPreviewBanner.tsx:5381": [
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "0"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "1"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "2"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "3"]
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"],
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "1"],
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "2"],
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "3"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "4"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "5"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "6"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "7"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "8"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "9"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "10"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "11"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "12"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "13"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "14"]
],
"public/app/features/dashboard-scene/saving/provisioned/ProvisionedResourceDeleteModal.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"],
@@ -3389,15 +3400,13 @@ exports[`better eslint`] = {
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "9"],
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "10"],
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "11"],
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "12"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "12"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "13"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "14"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "15"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "16"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "17"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "18"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "19"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "20"]
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "18"]
],
"public/app/features/dashboard-scene/saving/shared.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"],
@@ -54,7 +54,10 @@ func (c *ClientFactory) New(ns string) (*DynamicClient, KindsLookup, error) {
if err != nil {
return nil, nil, err
}
config := c.configProvider.GetRestConfig(ctx)
config, err := c.configProvider.GetRestConfig(ctx)
if err != nil {
return nil, nil, err
}
client, err := dynamic.NewForConfig(config)
if err != nil {
return nil, nil, err
@@ -79,7 +79,7 @@ export function DashboardScenePage({ route, queryParams, location }: Props) {
return (
<UrlSyncContextProvider scene={dashboard} updateUrlOnInit={true} createBrowserHistorySteps={true}>
<DashboardPreviewBanner queryParams={queryParams} route={route.routeName} path={path} />
<DashboardPreviewBanner queryParams={queryParams} route={route.routeName} slug={slug} path={path} />
<dashboard.Component model={dashboard} key={dashboard.state.key} />
<DashboardPrompt dashboard={dashboard} />
</UrlSyncContextProvider>
@@ -1,56 +1,80 @@
import { Alert, Icon, Stack } from '@grafana/ui';
import { DashboardPageRouteSearchParams } from 'app/features/dashboard/containers/types';
import { useGetRepositoryFilesWithPathQuery, useGetRepositoryQuery } from 'app/features/provisioning/api';
import { usePullRequestParam } from 'app/features/provisioning/hooks';
import { DashboardRoutes } from 'app/types';
interface DashboardPreviewBannerProps {
queryParams: DashboardPageRouteSearchParams;
route?: string;
slug?: string;
path?: string;
}
export function DashboardPreviewBanner({ queryParams, route, path }: DashboardPreviewBannerProps) {
const hasPrLink = Boolean(queryParams.prLink);
const isProvisioned = Boolean(route === DashboardRoutes.Provisioning && path);
if ('kiosk' in queryParams || (!queryParams.isPreview && !isProvisioned)) {
export function DashboardPreviewBanner({ queryParams, route, slug, path }: DashboardPreviewBannerProps) {
if ('kiosk' in queryParams || !path || route !== DashboardRoutes.Provisioning || !slug) {
return null;
}
const prParam = usePullRequestParam(); // pull_request_url
const file = useGetRepositoryFilesWithPathQuery({ name: slug, path, ref: queryParams.ref });
const title = getTitle({ isProvisioned, hasPR: hasPrLink });
return (
<Alert
title={title}
severity={isProvisioned ? 'info' : 'success'}
style={{ flex: 0 }}
buttonContent={
hasPrLink && (
// Required to create the new PR link
// TODO?? can the relevant links be in the file response???
const repo = useGetRepositoryQuery({ name: slug });
if (file.data?.errors) {
return <Alert title="error loading" severity="error" />;
}
console.log('BANNER', { queryParams, file: file.data, repo: repo.data });
// This page was loaded with a `pull_request_url` in the URL
// This is typically from an external link in a rendered pull request
if (prParam?.length) {
return (
<Alert
title={'This dashboard is loaded from a pull request in github.'}
severity={'info'}
style={{ flex: 0 }}
buttonContent={
<Stack alignItems={'center'}>
<span>{isProvisioned ? 'View' : 'Open'} pull request in GitHub</span>
<span>View pull request in GitHub</span>
<Icon name="external-link-alt" />
</Stack>
)
}
onRemove={hasPrLink ? () => window.open(queryParams.prLink, '_blank') : undefined}
>
{isProvisioned ? (
<>
The value is <strong>not</strong> saved in the grafana database.
</>
) : null}
}
onRemove={() => window.open(prParam, '_blank')}
>
The value is <strong>not yet</strong> saved in the grafana database
</Alert>
);
}
// Check if this is a github link
if (queryParams.ref?.length && repo.data?.spec?.type === 'github') {
const spec = repo.data?.spec.github!;
const url = `${spec.url}/compare/${spec.branch}...${queryParams.ref}?quick_pull=1&labels=grafana`;
return (
<Alert
title={'This dashboard is loaded from a branch in github.'}
severity={'info'}
style={{ flex: 0 }}
buttonContent={
<Stack alignItems={'center'}>
<span>Open pull request in GitHub</span>
<Icon name="external-link-alt" />
</Stack>
}
onRemove={() => window.open(url, '_blank')}
>
The value is <strong>not yet</strong> saved in the grafana database
</Alert>
);
}
return (
<Alert title={'This dashboard is loaded from an external repository'} severity={'info'} style={{ flex: 0 }}>
The value is <strong>not</strong> saved in the grafana database
</Alert>
);
}
type Opts = {
isProvisioned?: boolean;
hasPR: boolean;
};
function getTitle({ isProvisioned, hasPR }: Opts) {
if (isProvisioned) {
return 'This dashboard is loaded from an external repository';
}
if (hasPR) {
return "This dashboard is a draft. The changes aren't live yet. You can review them in GitHub.";
}
return 'This dashboard is a draft. The changes aren’t live yet. Submit a pull request for review.';
}
@@ -1,16 +1,19 @@
import { useEffect } from 'react';
import { Controller, useForm } from 'react-hook-form';
import { useNavigate } from 'react-router-dom-v5-compat';
import { AppEvents } from '@grafana/data';
import { getAppEvents, locationService } from '@grafana/runtime';
import { Alert, Button, Field, Input, RadioButtonGroup, Stack, TextArea, TextLink } from '@grafana/ui';
import { Alert, Button, Field, Input, RadioButtonGroup, Stack, TextArea } from '@grafana/ui';
import { FolderPicker } from 'app/core/components/Select/FolderPicker';
import { useUrlParams } from 'app/core/navigation/hooks';
import { AnnoKeyRepoName } from 'app/features/apiserver/types';
import { validationSrv } from 'app/features/manage-dashboards/services/ValidationSrv';
import { RepositoryView } from 'app/features/provisioning/api';
import { useCreateOrUpdateRepositoryFile, usePullRequestParam } from 'app/features/provisioning/hooks';
import { PROVISIONING_URL } from 'app/features/provisioning/constants';
import { useCreateOrUpdateRepositoryFile } from 'app/features/provisioning/hooks';
import { WorkflowOption } from 'app/features/provisioning/types';
import { createPRLink, validateBranchName } from 'app/features/provisioning/utils/git';
import { validateBranchName } from 'app/features/provisioning/utils/git';
import { DashboardScene } from '../../scene/DashboardScene';
import { SaveDashboardDrawer } from '../SaveDashboardDrawer';
@@ -42,7 +45,8 @@ export interface Props {
export function SaveProvisionedDashboard({ drawer, changeInfo, dashboard }: Props) {
const { meta, title: defaultTitle, description: defaultDescription } = dashboard.useState();
const prURL = usePullRequestParam();
const navigate = useNavigate();
const [params] = useUrlParams();
const {
values: defaultValues,
@@ -59,9 +63,9 @@ export function SaveProvisionedDashboard({ drawer, changeInfo, dashboard }: Prop
control,
setValue,
} = useForm<FormData>({ defaultValues });
const [title, ref, workflow, comment] = watch(['title', 'ref', 'workflow', 'comment']);
const [ref, workflow, path] = watch(['ref', 'workflow', 'path']);
const { isDirty } = dashboard.state;
const href = createPRLink(repositoryConfig, title, ref, comment);
useEffect(() => {
const appEvents = getAppEvents();
@@ -70,21 +74,34 @@ export function SaveProvisionedDashboard({ drawer, changeInfo, dashboard }: Prop
if (isNew) {
dashboard.setRepoName(defaultValues.repo);
}
const prLink = workflow !== 'write' ? href : undefined;
dashboard.closeModal();
locationService.partial({
viewPanel: null,
editPanel: null,
isPreview: true,
prLink,
});
if (workflow === 'branch' && ref !== '' && path !== '') {
// Redirect to the provisioning preview pages
navigate(`${PROVISIONING_URL}/${defaultValues.repo}/dashboard/preview/${path}?ref=${ref}`);
} else {
dashboard.closeModal();
locationService.partial({
viewPanel: null,
editPanel: null,
});
}
} else if (request.isError) {
appEvents.publish({
type: AppEvents.alertError.name,
payload: ['Error saving dashboard', request.error],
});
}
}, [request.isSuccess, request.isError, request.error, dashboard, workflow, href, isNew, defaultValues.repo]);
}, [
request.isSuccess,
request.isError,
request.error,
dashboard,
workflow,
isNew,
defaultValues.repo,
ref,
path,
navigate,
]);
useEffect(() => {
setValue('workflow', getDefaultWorkflow(repositoryConfig));
@@ -103,12 +120,14 @@ export function SaveProvisionedDashboard({ drawer, changeInfo, dashboard }: Prop
});
if (workflow === 'write') {
ref = undefined;
ref = params.get('ref') ?? undefined; // the original ref from URL or undefined
}
action({ ref, name: repo, path, message: comment, body: saveModel });
};
const workflows = getWorkflowOptions(repositoryConfig, ref);
return (
<form onSubmit={handleSubmit(doSave)}>
<Stack direction="column" gap={2}>
@@ -182,7 +201,7 @@ export function SaveProvisionedDashboard({ drawer, changeInfo, dashboard }: Prop
control={control}
name={'workflow'}
render={({ field: { ref, ...field } }) => {
return <RadioButtonGroup {...field} options={getWorkflowOptions(repositoryConfig)} />;
return <RadioButtonGroup {...field} options={workflows} />;
}}
/>
</Field>
@@ -198,15 +217,6 @@ export function SaveProvisionedDashboard({ drawer, changeInfo, dashboard }: Prop
)}
</>
)}
{prURL && (
<Alert severity="info" title="Pull request created">
A pull request has been created with changes to this dashboard:{' '}
<TextLink href={prURL} external>
{prURL}
</TextLink>
</Alert>
)}
<Stack gap={2}>
<Button variant="primary" type="submit" disabled={request.isLoading || !isDirty}>
{request.isLoading ? 'Saving...' : 'Save'}
@@ -5,13 +5,18 @@ export function getDefaultWorkflow(config?: RepositorySpec) {
return config?.workflows?.[0];
}
export function getWorkflowOptions(config?: RepositorySpec) {
export function getWorkflowOptions(config?: RepositorySpec, ref?: string) {
if (!config) {
return [];
}
if (config.local?.path) {
return [{ label: `Write to ${config.local.path}`, value: 'write' }];
}
let branch = ref ?? config.github?.branch;
const availableOptions: Array<{ label: string; value: WorkflowOption }> = [
{ label: `Push to ${config.github?.branch}`, value: 'write' },
{ label: `Push to ${branch ?? 'main'}`, value: 'write' },
{ label: 'Push to different branch', value: 'branch' },
];
@@ -20,8 +20,7 @@ export type DashboardPageRouteSearchParams = {
kiosk?: string | true;
scenes?: boolean;
shareView?: string;
isPreview?: boolean;
prLink?: string;
ref?: string; // used for repo preview
};
export type PublicDashboardPageRouteParams = {
@@ -1,11 +1,4 @@
import { Repository, RepositorySpec } from '../api';
export function createPRLink(spec?: RepositorySpec, dashboardName?: string, ref?: string, comment?: string) {
if (!spec || spec.type !== 'github' || !ref) {
return '';
}
return `${spec.github?.url}/compare/${spec.github?.branch}...${ref}?quick_pull=1&labels=grafana&title=Update dashboard ${dashboardName}&body=${encodeURI(comment || '')}`;
}
import { Repository } from '../api';
/**
* Validates a Git branch name according to the following rules: