update preview banner
This commit is contained in:
1 parent
57c46ffe71
commit
122cfb568b
8 files changed
+128
-85
No files matched your search
+17
-8
@@ -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.';
|
||||
}
|
||||
+36
-26
@@ -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:
|
||||
|
||||
Reference in new issue
Block a user