POC/Provisioning: Provisioned dashboard save refactor (#99505)
* refactor defaults * Only close the modal for direct workflow * Store prOpened state * Update betterer
This commit is contained in:
1 parent
78555be89d
commit
d21b9561e4
6 files changed
+118
-85
No files matched your search
+19
-19
@@ -3387,7 +3387,25 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "16"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "17"]
|
||||
],
|
||||
"public/app/features/dashboard-scene/saving/SaveProvisionedDashboard.tsx:5381": [
|
||||
"public/app/features/dashboard-scene/saving/SaveProvisionedDashboardForm.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. 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"]
|
||||
],
|
||||
"public/app/features/dashboard-scene/saving/getDashboardChanges.ts:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "1"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "2"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "3"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "4"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "5"]
|
||||
],
|
||||
"public/app/features/dashboard-scene/saving/provisioned/SaveProvisionedDashboard.tsx:5381": [
|
||||
[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"],
|
||||
@@ -3411,24 +3429,6 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "20"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "21"]
|
||||
],
|
||||
"public/app/features/dashboard-scene/saving/SaveProvisionedDashboardForm.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. 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"]
|
||||
],
|
||||
"public/app/features/dashboard-scene/saving/getDashboardChanges.ts:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "1"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "2"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "3"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "4"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "5"]
|
||||
],
|
||||
"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"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "1"]
|
||||
|
||||
@@ -9,8 +9,8 @@ import { DashboardScene } from '../scene/DashboardScene';
|
||||
|
||||
import { SaveDashboardAsForm } from './SaveDashboardAsForm';
|
||||
import { SaveDashboardForm } from './SaveDashboardForm';
|
||||
import { SaveProvisionedDashboard } from './SaveProvisionedDashboard';
|
||||
import { SaveProvisionedDashboardForm } from './SaveProvisionedDashboardForm';
|
||||
import { SaveProvisionedDashboard } from './provisioned/SaveProvisionedDashboard';
|
||||
|
||||
interface SaveDashboardDrawerState extends SceneObjectState {
|
||||
dashboardRef: SceneObjectRef<DashboardScene>;
|
||||
|
||||
+35
-64
@@ -1,23 +1,24 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
|
||||
import { AppEvents } from '@grafana/data';
|
||||
import { getAppEvents } from '@grafana/runtime';
|
||||
import { Alert, Button, Field, Icon, Input, RadioButtonGroup, Stack, TextArea, TextLink } from '@grafana/ui';
|
||||
import { FolderPicker } from 'app/core/components/Select/FolderPicker';
|
||||
import { AnnoKeyRepoName, AnnoKeyRepoPath } from 'app/features/apiserver/types';
|
||||
import { DashboardMeta } from 'app/types';
|
||||
import { AnnoKeyRepoName } from 'app/features/apiserver/types';
|
||||
import { validationSrv } from 'app/features/manage-dashboards/services/ValidationSrv';
|
||||
import { Repository } from 'app/features/provisioning/api';
|
||||
import { useCreateOrUpdateRepositoryFile, usePullRequestParam } from 'app/features/provisioning/hooks';
|
||||
import { WorkflowOption } from 'app/features/provisioning/types';
|
||||
import { createPRLink, validateBranchName } from 'app/features/provisioning/utils/git';
|
||||
|
||||
import { validationSrv } from '../../manage-dashboards/services/ValidationSrv';
|
||||
import { Repository, RepositorySpec } from '../../provisioning/api';
|
||||
import { useCreateOrUpdateRepositoryFile, useFolderRepository, usePullRequestParam } from '../../provisioning/hooks';
|
||||
import { WorkflowOption } from '../../provisioning/types';
|
||||
import { createPRLink, validateBranchName } from '../../provisioning/utils/git';
|
||||
import { DashboardScene } from '../scene/DashboardScene';
|
||||
import { DashboardScene } from '../../scene/DashboardScene';
|
||||
import { SaveDashboardDrawer } from '../SaveDashboardDrawer';
|
||||
import { SaveDashboardFormCommonOptions } from '../SaveDashboardForm';
|
||||
import { DashboardChangeInfo } from '../shared';
|
||||
|
||||
import { SaveDashboardDrawer } from './SaveDashboardDrawer';
|
||||
import { SaveDashboardFormCommonOptions } from './SaveDashboardForm';
|
||||
import { DashboardChangeInfo } from './shared';
|
||||
import { getDefaultWorkflow, getWorkflowOptions } from './defaults';
|
||||
import { useDefaultValues } from './hooks';
|
||||
|
||||
type FormData = {
|
||||
ref?: string;
|
||||
@@ -27,41 +28,12 @@ type FormData = {
|
||||
workflow?: WorkflowOption;
|
||||
title: string;
|
||||
description: string;
|
||||
folder: { uid?: string; title?: string };
|
||||
};
|
||||
|
||||
function getDefaultValues(meta: DashboardMeta, repositoryName = '') {
|
||||
const anno = meta.k8s?.annotations;
|
||||
const timestamp = Date.now();
|
||||
const ref = `dashboard/${timestamp}`;
|
||||
const pathName = meta.slug || `new-dashboard-${timestamp}`;
|
||||
let path = anno?.[AnnoKeyRepoPath] ?? `${pathName}.json`;
|
||||
const repo = anno?.[AnnoKeyRepoName] ?? repositoryName;
|
||||
const idx = path.indexOf('#');
|
||||
if (idx > 0) {
|
||||
path = path.substring(0, idx);
|
||||
}
|
||||
|
||||
return {
|
||||
ref,
|
||||
path,
|
||||
repo,
|
||||
comment: '',
|
||||
folder: { uid: meta.folderUid, title: '' },
|
||||
folder: {
|
||||
uid?: string;
|
||||
title?: string;
|
||||
};
|
||||
}
|
||||
|
||||
const getDefaultWorkflow = (config?: RepositorySpec) => {
|
||||
return config?.github?.branchWorkflow ? WorkflowOption.PullRequest : WorkflowOption.Direct;
|
||||
};
|
||||
|
||||
function getWorkflowOptions(branch = 'main') {
|
||||
return [
|
||||
{ label: `Commit to ${branch}`, value: WorkflowOption.Direct },
|
||||
{ label: 'Create pull request', value: WorkflowOption.PullRequest },
|
||||
];
|
||||
}
|
||||
|
||||
export interface Props {
|
||||
dashboard: DashboardScene;
|
||||
drawer: SaveDashboardDrawer;
|
||||
@@ -69,15 +41,16 @@ export interface Props {
|
||||
}
|
||||
|
||||
export function SaveProvisionedDashboard({ drawer, changeInfo, dashboard }: Props) {
|
||||
// Saving as a new provisioned dashboard
|
||||
const { saveProvisioned } = drawer.useState();
|
||||
const { meta, title: defaultTitle, description: defaultDescription } = dashboard.useState();
|
||||
const prURL = usePullRequestParam();
|
||||
const isNew = !meta.k8s?.annotations?.[AnnoKeyRepoPath];
|
||||
const folderRepository = useFolderRepository(meta.folderUid);
|
||||
const repositoryConfig = folderRepository?.spec;
|
||||
const repo = folderRepository?.metadata?.name;
|
||||
const defaultValues = getDefaultValues(meta, repo);
|
||||
const [prOpened, setPrOpened] = useState(false);
|
||||
const {
|
||||
values: defaultValues,
|
||||
isNew,
|
||||
isGitHub,
|
||||
repositoryConfig,
|
||||
} = useDefaultValues({ meta, defaultTitle, defaultDescription });
|
||||
const [action, request] = useCreateOrUpdateRepositoryFile(saveProvisioned || isNew ? undefined : defaultValues.path);
|
||||
const {
|
||||
register,
|
||||
@@ -86,18 +59,11 @@ export function SaveProvisionedDashboard({ drawer, changeInfo, dashboard }: Prop
|
||||
formState: { errors },
|
||||
control,
|
||||
setValue,
|
||||
} = useForm<FormData>({
|
||||
defaultValues: {
|
||||
...defaultValues,
|
||||
title: defaultTitle,
|
||||
description: defaultDescription,
|
||||
workflow: WorkflowOption.PullRequest,
|
||||
},
|
||||
});
|
||||
} = useForm<FormData>({ defaultValues });
|
||||
|
||||
const [title, ref, workflow, comment] = watch(['title', 'ref', 'workflow', 'comment']);
|
||||
const isGitHub = repositoryConfig?.type === 'github';
|
||||
const href = createPRLink(repositoryConfig, title, ref, comment);
|
||||
const { isDirty } = dashboard.state;
|
||||
const href = createPRLink(repositoryConfig, title, ref, comment);
|
||||
|
||||
useEffect(() => {
|
||||
const appEvents = getAppEvents();
|
||||
@@ -107,13 +73,18 @@ export function SaveProvisionedDashboard({ drawer, changeInfo, dashboard }: Prop
|
||||
payload: ['Dashboard saved'],
|
||||
});
|
||||
dashboard.setState({ isDirty: false });
|
||||
if (workflow === WorkflowOption.Direct) {
|
||||
dashboard.closeModal();
|
||||
} else {
|
||||
setPrOpened(true);
|
||||
}
|
||||
} else if (request.isError) {
|
||||
appEvents.publish({
|
||||
type: AppEvents.alertError.name,
|
||||
payload: ['Error saving dashboard', request.error],
|
||||
});
|
||||
}
|
||||
}, [request.isSuccess, request.isError, request.error, dashboard]);
|
||||
}, [request.isSuccess, request.isError, request.error, dashboard, workflow]);
|
||||
|
||||
useEffect(() => {
|
||||
setValue('workflow', getDefaultWorkflow(repositoryConfig));
|
||||
@@ -141,7 +112,7 @@ export function SaveProvisionedDashboard({ drawer, changeInfo, dashboard }: Prop
|
||||
return (
|
||||
<form onSubmit={handleSubmit(doSave)}>
|
||||
<Stack direction="column" gap={2}>
|
||||
{isGitHub && workflow === WorkflowOption.PullRequest && !isDirty && (
|
||||
{prOpened && href && (
|
||||
<Alert
|
||||
severity="success"
|
||||
title="Branch successfully created"
|
||||
@@ -153,6 +124,7 @@ export function SaveProvisionedDashboard({ drawer, changeInfo, dashboard }: Prop
|
||||
}
|
||||
onRemove={() => {
|
||||
window.open(href, '_blank');
|
||||
dashboard.closeModal();
|
||||
}}
|
||||
>
|
||||
You can now open a pull request in Github.
|
||||
@@ -222,7 +194,7 @@ export function SaveProvisionedDashboard({ drawer, changeInfo, dashboard }: Prop
|
||||
control={control}
|
||||
name={'workflow'}
|
||||
render={({ field: { ref, ...field } }) => {
|
||||
return <RadioButtonGroup {...field} options={getWorkflowOptions(repositoryConfig.github?.branch)} />;
|
||||
return <RadioButtonGroup {...field} options={getWorkflowOptions(repositoryConfig?.github?.branch)} />;
|
||||
}}
|
||||
/>
|
||||
</Field>
|
||||
@@ -272,7 +244,6 @@ const BranchValidationError = () => {
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
async function validateDashboardName(title: string, formValues: FormData) {
|
||||
if (title === formValues.folder.title?.trim()) {
|
||||
return 'Dashboard name cannot be the same as folder name';
|
||||
@@ -0,0 +1,13 @@
|
||||
import { RepositorySpec } from 'app/features/provisioning/api';
|
||||
import { WorkflowOption } from 'app/features/provisioning/types';
|
||||
|
||||
export function getDefaultWorkflow(config?: RepositorySpec) {
|
||||
return config?.github?.branchWorkflow ? WorkflowOption.PullRequest : WorkflowOption.Direct;
|
||||
}
|
||||
|
||||
export function getWorkflowOptions(branch = 'main') {
|
||||
return [
|
||||
{ label: `Commit to ${branch}`, value: WorkflowOption.Direct },
|
||||
{ label: 'Create pull request', value: WorkflowOption.PullRequest },
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import { AnnoKeyRepoName, AnnoKeyRepoPath } from 'app/features/apiserver/types';
|
||||
import { useFolderRepository } from 'app/features/provisioning/hooks';
|
||||
import { DashboardMeta } from 'app/types';
|
||||
|
||||
import { getDefaultWorkflow } from './defaults';
|
||||
|
||||
interface UseDefaultValuesParams {
|
||||
meta: DashboardMeta;
|
||||
defaultTitle: string;
|
||||
defaultDescription?: string;
|
||||
}
|
||||
|
||||
function generatePath(timestamp: number, pathFromAnnotation?: string, slug?: string) {
|
||||
if (pathFromAnnotation) {
|
||||
const hashIndex = pathFromAnnotation.indexOf('#');
|
||||
return hashIndex > 0 ? pathFromAnnotation.substring(0, hashIndex) : pathFromAnnotation;
|
||||
}
|
||||
|
||||
const pathSlug = slug || `new-dashboard-${timestamp}`;
|
||||
return `${pathSlug}.json`;
|
||||
}
|
||||
|
||||
export function useDefaultValues({ meta, defaultTitle, defaultDescription }: UseDefaultValuesParams) {
|
||||
const folderRepository = useFolderRepository(meta.folderUid);
|
||||
const timestamp = Date.now();
|
||||
const annotations = meta.k8s?.annotations;
|
||||
const annoPath = annotations?.[AnnoKeyRepoPath];
|
||||
const repositoryConfig = folderRepository?.spec;
|
||||
const repositoryName = folderRepository?.metadata?.name ?? '';
|
||||
|
||||
return {
|
||||
values: {
|
||||
ref: `dashboard/${timestamp}`,
|
||||
path: generatePath(timestamp, annoPath, meta.slug),
|
||||
repo: annotations?.[AnnoKeyRepoName] ?? repositoryName,
|
||||
comment: '',
|
||||
folder: {
|
||||
uid: meta.folderUid,
|
||||
title: '',
|
||||
},
|
||||
title: defaultTitle,
|
||||
description: defaultDescription ?? '',
|
||||
workflow: getDefaultWorkflow(repositoryConfig),
|
||||
},
|
||||
isNew: !annoPath,
|
||||
repositoryConfig,
|
||||
isGitHub: repositoryConfig?.type === 'github',
|
||||
};
|
||||
}
|
||||
@@ -614,7 +614,7 @@ export function ToolbarActions({ dashboard }: Props) {
|
||||
}
|
||||
|
||||
// If we only can save as copy
|
||||
if (canSaveAs && !meta.canSave && !meta.canMakeEditable) {
|
||||
if (canSaveAs && !meta.canSave && !meta.canMakeEditable && !isProvisionedNG) {
|
||||
return (
|
||||
<Button
|
||||
onClick={() => {
|
||||
|
||||
Reference in new issue
Block a user