SaveProvisionedDashboard: Provisioned dashboard "Save as copy" flow (#114435)
* SaveProvisionedDashboard: Save as copy flow * add copy tags toggle
This commit is contained in:
@@ -102,7 +102,14 @@ function SaveDashboardDrawerComponent({ model }: SceneComponentProps<SaveDashboa
|
||||
}
|
||||
|
||||
if (isProvisionedNG) {
|
||||
return <SaveProvisionedDashboard dashboard={dashboard} changeInfo={changeInfo} drawer={model} />;
|
||||
return (
|
||||
<SaveProvisionedDashboard
|
||||
dashboard={dashboard}
|
||||
changeInfo={changeInfo}
|
||||
drawer={model}
|
||||
saveAsCopy={saveAsCopy}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (saveAsCopy || changeInfo.isNew) {
|
||||
|
||||
@@ -835,7 +835,7 @@ export class DashboardScene extends SceneObjectBase<DashboardSceneState> impleme
|
||||
kind: 'Dashboard',
|
||||
metadata: {
|
||||
...meta.k8s,
|
||||
name: meta.uid ?? meta.k8s?.name,
|
||||
name: options.isNew ? undefined : (meta.uid ?? meta.k8s?.name),
|
||||
generateName: options.isNew ? 'd' : undefined,
|
||||
},
|
||||
spec,
|
||||
|
||||
@@ -178,7 +178,7 @@ export class V1DashboardSerializer
|
||||
uid: '',
|
||||
title: options.title || '',
|
||||
description: options.description || undefined,
|
||||
tags: options.isNew || options.copyTags ? saveModel.tags : [],
|
||||
tags: options.copyTags === false ? [] : saveModel.tags,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -390,7 +390,7 @@ export class V2DashboardSerializer
|
||||
...saveModel,
|
||||
title: options.title || '',
|
||||
description: options.description || '',
|
||||
tags: options.isNew || options.copyTags ? saveModel.tags : [],
|
||||
tags: options.copyTags === false ? [] : saveModel.tags,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -10,10 +10,14 @@ export interface SaveProvisionedDashboardProps {
|
||||
dashboard: DashboardScene;
|
||||
drawer: SaveDashboardDrawer;
|
||||
changeInfo: DashboardChangeInfo;
|
||||
saveAsCopy?: boolean;
|
||||
}
|
||||
|
||||
export function SaveProvisionedDashboard({ drawer, changeInfo, dashboard }: SaveProvisionedDashboardProps) {
|
||||
const { isNew, defaultValues, workflowOptions, readOnly, repository } = useProvisionedDashboardData(dashboard);
|
||||
export function SaveProvisionedDashboard({ drawer, changeInfo, dashboard, saveAsCopy }: SaveProvisionedDashboardProps) {
|
||||
const { isNew, defaultValues, workflowOptions, readOnly, repository } = useProvisionedDashboardData(
|
||||
dashboard,
|
||||
saveAsCopy
|
||||
);
|
||||
|
||||
if (!defaultValues) {
|
||||
return null;
|
||||
@@ -24,11 +28,12 @@ export function SaveProvisionedDashboard({ drawer, changeInfo, dashboard }: Save
|
||||
dashboard={dashboard}
|
||||
drawer={drawer}
|
||||
changeInfo={changeInfo}
|
||||
isNew={isNew}
|
||||
isNew={isNew || !!saveAsCopy}
|
||||
defaultValues={defaultValues}
|
||||
repository={repository}
|
||||
workflowOptions={workflowOptions}
|
||||
readOnly={readOnly}
|
||||
saveAsCopy={saveAsCopy}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
+19
-3
@@ -6,7 +6,7 @@ import { AppEvents, locationUtil } from '@grafana/data';
|
||||
import { Trans, t } from '@grafana/i18n';
|
||||
import { getAppEvents, locationService, reportInteraction } from '@grafana/runtime';
|
||||
import { Dashboard } from '@grafana/schema';
|
||||
import { Button, Field, Input, Stack, TextArea } from '@grafana/ui';
|
||||
import { Button, Field, Input, Stack, TextArea, Switch } from '@grafana/ui';
|
||||
import { RepositoryView, Unstructured } from 'app/api/clients/provisioning/v0alpha1';
|
||||
import kbn from 'app/core/utils/kbn';
|
||||
import { Resource } from 'app/features/apiserver/types';
|
||||
@@ -47,6 +47,7 @@ export function SaveProvisionedDashboardForm({
|
||||
workflowOptions,
|
||||
readOnly,
|
||||
repository,
|
||||
saveAsCopy,
|
||||
}: Props) {
|
||||
const navigate = useNavigate();
|
||||
const appEvents = getAppEvents();
|
||||
@@ -166,7 +167,15 @@ export function SaveProvisionedDashboardForm({
|
||||
});
|
||||
|
||||
// Submit handler for saving the form data
|
||||
const handleFormSubmit = async ({ title, description, repo, path, comment, ref }: ProvisionedDashboardFormData) => {
|
||||
const handleFormSubmit = async ({
|
||||
title,
|
||||
description,
|
||||
repo,
|
||||
path,
|
||||
comment,
|
||||
ref,
|
||||
copyTags,
|
||||
}: ProvisionedDashboardFormData) => {
|
||||
// Validate required fields
|
||||
if (!repo || !path) {
|
||||
console.error('Missing required fields for saving:', { repo, path });
|
||||
@@ -185,7 +194,8 @@ export function SaveProvisionedDashboardForm({
|
||||
isNew,
|
||||
title,
|
||||
description,
|
||||
copyTags: true,
|
||||
copyTags,
|
||||
saveAsCopy,
|
||||
});
|
||||
|
||||
reportInteraction('grafana_provisioning_dashboard_save_submitted', {
|
||||
@@ -287,6 +297,12 @@ export function SaveProvisionedDashboardForm({
|
||||
isNew={isNew}
|
||||
/>
|
||||
|
||||
{saveAsCopy && (
|
||||
<Field noMargin label={t('dashboard-scene.save-dashboard-as-form.label-copy-tags', 'Copy tags')}>
|
||||
<Switch {...register('copyTags')} />
|
||||
</Field>
|
||||
)}
|
||||
|
||||
<Stack gap={2}>
|
||||
<Button variant="secondary" onClick={drawer.onClose} fill="outline">
|
||||
<Trans i18nKey="dashboard-scene.save-provisioned-dashboard-form.cancel">Cancel</Trans>
|
||||
|
||||
@@ -18,9 +18,16 @@ interface UseDefaultValuesParams {
|
||||
defaultTitle: string;
|
||||
defaultDescription?: string;
|
||||
loadedFromRef?: string;
|
||||
saveAsCopy?: boolean;
|
||||
}
|
||||
|
||||
export function useDefaultValues({ meta, defaultTitle, defaultDescription, loadedFromRef }: UseDefaultValuesParams) {
|
||||
export function useDefaultValues({
|
||||
meta,
|
||||
defaultTitle,
|
||||
defaultDescription,
|
||||
loadedFromRef,
|
||||
saveAsCopy,
|
||||
}: UseDefaultValuesParams) {
|
||||
const annotations = meta.k8s?.annotations;
|
||||
const managerKind = annotations?.[AnnoKeyManagerKind];
|
||||
const managerIdentity = annotations?.[AnnoKeyManagerIdentity];
|
||||
@@ -35,8 +42,8 @@ export function useDefaultValues({ meta, defaultTitle, defaultDescription, loade
|
||||
|
||||
const dashboardPath = generatePath({
|
||||
timestamp,
|
||||
pathFromAnnotation: sourcePath,
|
||||
slug: meta.slug,
|
||||
pathFromAnnotation: saveAsCopy ? undefined : sourcePath,
|
||||
slug: saveAsCopy ? undefined : meta.slug,
|
||||
folderPath,
|
||||
});
|
||||
|
||||
@@ -56,9 +63,10 @@ export function useDefaultValues({ meta, defaultTitle, defaultDescription, loade
|
||||
uid: meta.folderUid,
|
||||
title: '',
|
||||
},
|
||||
title: defaultTitle,
|
||||
title: saveAsCopy ? `${defaultTitle} Copy` : defaultTitle,
|
||||
description: defaultDescription ?? '',
|
||||
workflow: getDefaultWorkflow(repository, loadedFromRef),
|
||||
copyTags: saveAsCopy ? false : true,
|
||||
},
|
||||
isNew: !meta.k8s?.name,
|
||||
repository,
|
||||
@@ -82,7 +90,7 @@ export interface ProvisionedDashboardData {
|
||||
* It retrieves default values, repository information, and workflow options based on the current dashboard state.
|
||||
*/
|
||||
|
||||
export function useProvisionedDashboardData(dashboard: DashboardScene): ProvisionedDashboardData {
|
||||
export function useProvisionedDashboardData(dashboard: DashboardScene, saveAsCopy?: boolean): ProvisionedDashboardData {
|
||||
const { meta, title: defaultTitle, description: defaultDescription } = dashboard.useState();
|
||||
const [params] = useUrlParams();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
@@ -93,6 +101,7 @@ export function useProvisionedDashboardData(dashboard: DashboardScene): Provisio
|
||||
defaultTitle,
|
||||
defaultDescription,
|
||||
loadedFromRef,
|
||||
saveAsCopy,
|
||||
});
|
||||
|
||||
if (!defaultValuesResult) {
|
||||
|
||||
@@ -15,4 +15,5 @@ export interface ProvisionedDashboardFormData extends BaseProvisionedFormData {
|
||||
uid?: string;
|
||||
title?: string;
|
||||
};
|
||||
copyTags?: boolean;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user