mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Detect folder changes when saving a dashboard (#85378)
This commit is contained in:
parent
6a75a8f354
commit
d983629650
@ -2489,10 +2489,6 @@ exports[`better eslint`] = {
|
|||||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
[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.", "1"]
|
||||||
],
|
],
|
||||||
"public/app/features/dashboard-scene/saving/getSaveDashboardChange.ts:5381": [
|
|
||||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
|
||||||
[0, 0, 0, "Do not use any type assertions.", "1"]
|
|
||||||
],
|
|
||||||
"public/app/features/dashboard-scene/scene/PanelMenuBehavior.tsx:5381": [
|
"public/app/features/dashboard-scene/scene/PanelMenuBehavior.tsx:5381": [
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||||
|
@ -35,61 +35,65 @@ export class DashboardSceneChangeTracker {
|
|||||||
Object.prototype.hasOwnProperty.call(payload.partialUpdate, 'intervals') ||
|
Object.prototype.hasOwnProperty.call(payload.partialUpdate, 'intervals') ||
|
||||||
Object.prototype.hasOwnProperty.call(payload.partialUpdate, 'refresh')
|
Object.prototype.hasOwnProperty.call(payload.partialUpdate, 'refresh')
|
||||||
) {
|
) {
|
||||||
this.detectChanges();
|
this.detectSaveModelChanges();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (payload.changedObject instanceof behaviors.CursorSync) {
|
if (payload.changedObject instanceof behaviors.CursorSync) {
|
||||||
this.detectChanges();
|
this.detectSaveModelChanges();
|
||||||
}
|
}
|
||||||
if (payload.changedObject instanceof SceneDataLayerSet) {
|
if (payload.changedObject instanceof SceneDataLayerSet) {
|
||||||
this.detectChanges();
|
this.detectSaveModelChanges();
|
||||||
}
|
}
|
||||||
if (payload.changedObject instanceof DashboardGridItem) {
|
if (payload.changedObject instanceof DashboardGridItem) {
|
||||||
this.detectChanges();
|
this.detectSaveModelChanges();
|
||||||
}
|
}
|
||||||
if (payload.changedObject instanceof SceneGridLayout) {
|
if (payload.changedObject instanceof SceneGridLayout) {
|
||||||
this.detectChanges();
|
this.detectSaveModelChanges();
|
||||||
}
|
}
|
||||||
if (payload.changedObject instanceof DashboardScene) {
|
if (payload.changedObject instanceof DashboardScene) {
|
||||||
if (Object.keys(payload.partialUpdate).some((key) => PERSISTED_PROPS.includes(key))) {
|
if (Object.keys(payload.partialUpdate).some((key) => PERSISTED_PROPS.includes(key))) {
|
||||||
this.detectChanges();
|
this.detectSaveModelChanges();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (payload.changedObject instanceof SceneTimeRange) {
|
if (payload.changedObject instanceof SceneTimeRange) {
|
||||||
this.detectChanges();
|
this.detectSaveModelChanges();
|
||||||
}
|
}
|
||||||
if (payload.changedObject instanceof DashboardControls) {
|
if (payload.changedObject instanceof DashboardControls) {
|
||||||
if (Object.prototype.hasOwnProperty.call(payload.partialUpdate, 'hideTimeControls')) {
|
if (Object.prototype.hasOwnProperty.call(payload.partialUpdate, 'hideTimeControls')) {
|
||||||
this.detectChanges();
|
this.detectSaveModelChanges();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (payload.changedObject instanceof SceneVariableSet) {
|
if (payload.changedObject instanceof SceneVariableSet) {
|
||||||
this.detectChanges();
|
this.detectSaveModelChanges();
|
||||||
}
|
}
|
||||||
if (payload.changedObject instanceof DashboardAnnotationsDataLayer) {
|
if (payload.changedObject instanceof DashboardAnnotationsDataLayer) {
|
||||||
if (!Object.prototype.hasOwnProperty.call(payload.partialUpdate, 'data')) {
|
if (!Object.prototype.hasOwnProperty.call(payload.partialUpdate, 'data')) {
|
||||||
this.detectChanges();
|
this.detectSaveModelChanges();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (payload.changedObject instanceof behaviors.LiveNowTimer) {
|
if (payload.changedObject instanceof behaviors.LiveNowTimer) {
|
||||||
this.detectChanges();
|
this.detectSaveModelChanges();
|
||||||
}
|
}
|
||||||
if (isSceneVariableInstance(payload.changedObject)) {
|
if (isSceneVariableInstance(payload.changedObject)) {
|
||||||
this.detectChanges();
|
this.detectSaveModelChanges();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private detectChanges() {
|
private detectSaveModelChanges() {
|
||||||
this._changesWorker?.postMessage({
|
this._changesWorker?.postMessage({
|
||||||
changed: transformSceneToSaveModel(this._dashboard),
|
changed: transformSceneToSaveModel(this._dashboard),
|
||||||
initial: this._dashboard.getInitialSaveModel(),
|
initial: this._dashboard.getInitialSaveModel(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private hasMetadataChanges() {
|
||||||
|
return this._dashboard.state.meta.folderUid !== this._dashboard.getInitialState()?.meta.folderUid;
|
||||||
|
}
|
||||||
|
|
||||||
private updateIsDirty(result: DashboardChangeInfo) {
|
private updateIsDirty(result: DashboardChangeInfo) {
|
||||||
const { hasChanges } = result;
|
const { hasChanges } = result;
|
||||||
|
|
||||||
if (hasChanges) {
|
if (hasChanges || this.hasMetadataChanges()) {
|
||||||
if (!this._dashboard.state.isDirty) {
|
if (!this._dashboard.state.isDirty) {
|
||||||
this._dashboard.setState({ isDirty: true });
|
this._dashboard.setState({ isDirty: true });
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ import { validationSrv } from 'app/features/manage-dashboards/services/Validatio
|
|||||||
|
|
||||||
import { DashboardScene } from '../scene/DashboardScene';
|
import { DashboardScene } from '../scene/DashboardScene';
|
||||||
|
|
||||||
import { SaveDashboardDrawer } from './SaveDashboardDrawer';
|
|
||||||
import { DashboardChangeInfo, NameAlreadyExistsError, SaveButton, isNameExistsError } from './shared';
|
import { DashboardChangeInfo, NameAlreadyExistsError, SaveButton, isNameExistsError } from './shared';
|
||||||
import { useSaveDashboard } from './useSaveDashboard';
|
import { useSaveDashboard } from './useSaveDashboard';
|
||||||
|
|
||||||
@ -23,11 +22,10 @@ interface SaveDashboardAsFormDTO {
|
|||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
dashboard: DashboardScene;
|
dashboard: DashboardScene;
|
||||||
drawer: SaveDashboardDrawer;
|
|
||||||
changeInfo: DashboardChangeInfo;
|
changeInfo: DashboardChangeInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SaveDashboardAsForm({ dashboard, drawer, changeInfo }: Props) {
|
export function SaveDashboardAsForm({ dashboard, changeInfo }: Props) {
|
||||||
const { changedSaveModel } = changeInfo;
|
const { changedSaveModel } = changeInfo;
|
||||||
|
|
||||||
const { register, handleSubmit, setValue, formState, getValues, watch, trigger } = useForm<SaveDashboardAsFormDTO>({
|
const { register, handleSubmit, setValue, formState, getValues, watch, trigger } = useForm<SaveDashboardAsFormDTO>({
|
||||||
|
@ -46,19 +46,21 @@ export class SaveDashboardDrawer extends SceneObjectBase<SaveDashboardDrawerStat
|
|||||||
saveVariables,
|
saveVariables,
|
||||||
saveRefresh
|
saveRefresh
|
||||||
);
|
);
|
||||||
const { changedSaveModel, initialSaveModel, diffs, diffCount } = changeInfo;
|
const { changedSaveModel, initialSaveModel, diffs, diffCount, hasFolderChanges } = changeInfo;
|
||||||
|
const changesCount = diffCount + (hasFolderChanges ? 1 : 0);
|
||||||
const dashboard = model.state.dashboardRef.resolve();
|
const dashboard = model.state.dashboardRef.resolve();
|
||||||
const isProvisioned = dashboard.state.meta.provisioned;
|
const { meta } = dashboard.useState();
|
||||||
|
const { provisioned: isProvisioned, folderTitle } = meta;
|
||||||
|
|
||||||
const tabs = (
|
const tabs = (
|
||||||
<TabsBar>
|
<TabsBar>
|
||||||
<Tab label={'Details'} active={!showDiff} onChangeTab={() => model.setState({ showDiff: false })} />
|
<Tab label={'Details'} active={!showDiff} onChangeTab={() => model.setState({ showDiff: false })} />
|
||||||
{diffCount > 0 && (
|
{changesCount > 0 && (
|
||||||
<Tab
|
<Tab
|
||||||
label={'Changes'}
|
label={'Changes'}
|
||||||
active={showDiff}
|
active={showDiff}
|
||||||
onChangeTab={() => model.setState({ showDiff: true })}
|
onChangeTab={() => model.setState({ showDiff: true })}
|
||||||
counter={diffCount}
|
counter={changesCount}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</TabsBar>
|
</TabsBar>
|
||||||
@ -73,11 +75,20 @@ export class SaveDashboardDrawer extends SceneObjectBase<SaveDashboardDrawerStat
|
|||||||
|
|
||||||
const renderBody = () => {
|
const renderBody = () => {
|
||||||
if (showDiff) {
|
if (showDiff) {
|
||||||
return <SaveDashboardDiff diff={diffs} oldValue={initialSaveModel} newValue={changedSaveModel} />;
|
return (
|
||||||
|
<SaveDashboardDiff
|
||||||
|
diff={diffs}
|
||||||
|
oldValue={initialSaveModel}
|
||||||
|
newValue={changedSaveModel}
|
||||||
|
hasFolderChanges={hasFolderChanges}
|
||||||
|
oldFolder={dashboard.getInitialState()?.meta.folderTitle}
|
||||||
|
newFolder={folderTitle}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (saveAsCopy || changeInfo.isNew) {
|
if (saveAsCopy || changeInfo.isNew) {
|
||||||
return <SaveDashboardAsForm dashboard={dashboard} changeInfo={changeInfo} drawer={model} />;
|
return <SaveDashboardAsForm dashboard={dashboard} changeInfo={changeInfo} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isProvisioned) {
|
if (isProvisioned) {
|
||||||
|
@ -104,7 +104,6 @@ export function SaveDashboardForm({ dashboard, drawer, changeInfo }: Props) {
|
|||||||
<Stack gap={0} direction="column">
|
<Stack gap={0} direction="column">
|
||||||
<SaveDashboardFormCommonOptions drawer={drawer} changeInfo={changeInfo} />
|
<SaveDashboardFormCommonOptions drawer={drawer} changeInfo={changeInfo} />
|
||||||
<Field label="Message">
|
<Field label="Message">
|
||||||
{/* config.featureToggles.dashgpt * TOOD GenAIDashboardChangesButton */}
|
|
||||||
<TextArea
|
<TextArea
|
||||||
aria-label="message"
|
aria-label="message"
|
||||||
value={options.message ?? ''}
|
value={options.message ?? ''}
|
||||||
|
@ -44,6 +44,17 @@ describe('getDashboardChangesFromScene', () => {
|
|||||||
expect(result.diffCount).toBe(1);
|
expect(result.diffCount).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Can detect folder change', () => {
|
||||||
|
const dashboard = setup();
|
||||||
|
|
||||||
|
dashboard.state.meta.folderUid = 'folder-2';
|
||||||
|
|
||||||
|
const result = getDashboardChangesFromScene(dashboard, false);
|
||||||
|
expect(result.hasChanges).toBe(true);
|
||||||
|
expect(result.diffCount).toBe(0); // Diff count is 0 because the diff contemplate only the model
|
||||||
|
expect(result.hasFolderChanges).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
it('Can detect refresh changed', () => {
|
it('Can detect refresh changed', () => {
|
||||||
const dashboard = setup();
|
const dashboard = setup();
|
||||||
|
|
||||||
|
@ -1,20 +1,35 @@
|
|||||||
import { DashboardScene } from '../scene/DashboardScene';
|
import { DashboardScene } from '../scene/DashboardScene';
|
||||||
import { transformSceneToSaveModel } from '../serialization/transformSceneToSaveModel';
|
import { transformSceneToSaveModel } from '../serialization/transformSceneToSaveModel';
|
||||||
|
|
||||||
import { getDashboardChanges } from './getDashboardChanges';
|
import { getDashboardChanges as getDashboardSaveModelChanges } from './getDashboardChanges';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get changes between the initial save model and the current scene.
|
||||||
|
* It also checks if the folder has changed.
|
||||||
|
* @param scene DashboardScene object
|
||||||
|
* @param saveTimeRange if true, compare the time range
|
||||||
|
* @param saveVariables if true, compare the variables
|
||||||
|
* @param saveRefresh if true, compare the refresh interval
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
export function getDashboardChangesFromScene(
|
export function getDashboardChangesFromScene(
|
||||||
scene: DashboardScene,
|
scene: DashboardScene,
|
||||||
saveTimeRange?: boolean,
|
saveTimeRange?: boolean,
|
||||||
saveVariables?: boolean,
|
saveVariables?: boolean,
|
||||||
saveRefresh?: boolean
|
saveRefresh?: boolean
|
||||||
) {
|
) {
|
||||||
const changeInfo = getDashboardChanges(
|
const changeInfo = getDashboardSaveModelChanges(
|
||||||
scene.getInitialSaveModel()!,
|
scene.getInitialSaveModel()!,
|
||||||
transformSceneToSaveModel(scene),
|
transformSceneToSaveModel(scene),
|
||||||
saveTimeRange,
|
saveTimeRange,
|
||||||
saveVariables,
|
saveVariables,
|
||||||
saveRefresh
|
saveRefresh
|
||||||
);
|
);
|
||||||
return changeInfo;
|
const hasFolderChanges = scene.getInitialState()?.meta.folderUid !== scene.state.meta.folderUid;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...changeInfo,
|
||||||
|
hasFolderChanges,
|
||||||
|
hasChanges: changeInfo.hasChanges || hasFolderChanges,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,95 +0,0 @@
|
|||||||
import { isEqual } from 'lodash';
|
|
||||||
|
|
||||||
import { AdHocVariableModel, TypedVariableModel } from '@grafana/data';
|
|
||||||
import { Dashboard } from '@grafana/schema';
|
|
||||||
|
|
||||||
import { DashboardScene } from '../scene/DashboardScene';
|
|
||||||
import { transformSceneToSaveModel } from '../serialization/transformSceneToSaveModel';
|
|
||||||
import { jsonDiff } from '../settings/version-history/utils';
|
|
||||||
|
|
||||||
import { DashboardChangeInfo } from './shared';
|
|
||||||
|
|
||||||
export function getSaveDashboardChange(
|
|
||||||
dashboard: DashboardScene,
|
|
||||||
saveTimeRange?: boolean,
|
|
||||||
saveVariables?: boolean,
|
|
||||||
saveRefresh?: boolean
|
|
||||||
): DashboardChangeInfo {
|
|
||||||
const initialSaveModel = dashboard.getInitialSaveModel()!;
|
|
||||||
|
|
||||||
if (dashboard.state.editPanel) {
|
|
||||||
dashboard.state.editPanel.commitChanges();
|
|
||||||
}
|
|
||||||
|
|
||||||
const changedSaveModel = transformSceneToSaveModel(dashboard);
|
|
||||||
const hasTimeChanged = getHasTimeChanged(changedSaveModel, initialSaveModel);
|
|
||||||
|
|
||||||
const hasVariableValueChanges = applyVariableChanges(changedSaveModel, initialSaveModel, saveVariables);
|
|
||||||
const hasRefreshChanged = changedSaveModel.refresh !== initialSaveModel.refresh;
|
|
||||||
|
|
||||||
if (!saveTimeRange) {
|
|
||||||
changedSaveModel.time = initialSaveModel.time;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!saveRefresh) {
|
|
||||||
changedSaveModel.refresh = initialSaveModel.refresh;
|
|
||||||
}
|
|
||||||
|
|
||||||
const diff = jsonDiff(initialSaveModel, changedSaveModel);
|
|
||||||
|
|
||||||
let diffCount = 0;
|
|
||||||
for (const d of Object.values(diff)) {
|
|
||||||
diffCount += d.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
changedSaveModel,
|
|
||||||
initialSaveModel,
|
|
||||||
diffs: diff,
|
|
||||||
diffCount,
|
|
||||||
hasChanges: diffCount > 0,
|
|
||||||
hasTimeChanges: hasTimeChanged,
|
|
||||||
isNew: changedSaveModel.version === 0,
|
|
||||||
hasVariableValueChanges,
|
|
||||||
hasRefreshChange: hasRefreshChanged,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getHasTimeChanged(saveModel: Dashboard, originalSaveModel: Dashboard) {
|
|
||||||
return saveModel.time?.from !== originalSaveModel.time?.from || saveModel.time?.to !== originalSaveModel.time?.to;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function applyVariableChanges(saveModel: Dashboard, originalSaveModel: Dashboard, saveVariables?: boolean) {
|
|
||||||
const originalVariables = originalSaveModel.templating?.list ?? [];
|
|
||||||
const variablesToSave = saveModel.templating?.list ?? [];
|
|
||||||
let hasVariableValueChanges = false;
|
|
||||||
|
|
||||||
for (const variable of variablesToSave) {
|
|
||||||
const original = originalVariables.find(({ name, type }) => name === variable.name && type === variable.type);
|
|
||||||
|
|
||||||
if (!original) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Old schema property that never should be in persisted model
|
|
||||||
if (original.current && Object.hasOwn(original.current, 'selected')) {
|
|
||||||
delete original.current.selected;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isEqual(variable.current, original.current)) {
|
|
||||||
hasVariableValueChanges = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!saveVariables) {
|
|
||||||
const typed = variable as TypedVariableModel;
|
|
||||||
if (typed.type === 'adhoc') {
|
|
||||||
typed.filters = (original as AdHocVariableModel).filters;
|
|
||||||
} else {
|
|
||||||
variable.current = original.current;
|
|
||||||
variable.options = original.options;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return hasVariableValueChanges;
|
|
||||||
}
|
|
@ -17,6 +17,7 @@ export interface DashboardChangeInfo {
|
|||||||
hasVariableValueChanges: boolean;
|
hasVariableValueChanges: boolean;
|
||||||
hasRefreshChange: boolean;
|
hasRefreshChange: boolean;
|
||||||
isNew?: boolean;
|
isNew?: boolean;
|
||||||
|
hasFolderChanges?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isVersionMismatchError(error?: Error) {
|
export function isVersionMismatchError(error?: Error) {
|
||||||
|
@ -175,7 +175,6 @@ describe('DashboardScene', () => {
|
|||||||
${'tags'} | ${['tag3', 'tag4']}
|
${'tags'} | ${['tag3', 'tag4']}
|
||||||
${'editable'} | ${false}
|
${'editable'} | ${false}
|
||||||
${'links'} | ${[]}
|
${'links'} | ${[]}
|
||||||
${'meta'} | ${{ folderUid: 'new-folder-uid', folderTitle: 'new-folder-title', hasUnsavedFolderChange: true }}
|
|
||||||
`(
|
`(
|
||||||
'A change to $prop should set isDirty true',
|
'A change to $prop should set isDirty true',
|
||||||
({ prop, value }: { prop: keyof DashboardSceneState; value: unknown }) => {
|
({ prop, value }: { prop: keyof DashboardSceneState; value: unknown }) => {
|
||||||
@ -189,6 +188,26 @@ describe('DashboardScene', () => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
it('A change to folderUid should set isDirty true', () => {
|
||||||
|
const prevMeta = { ...scene.state.meta };
|
||||||
|
|
||||||
|
// The worker only detects changes in the model, so the folder change should be detected anyway
|
||||||
|
mockResultsOfDetectChangesWorker({ hasChanges: false, hasTimeChanges: false, hasVariableValueChanges: false });
|
||||||
|
|
||||||
|
scene.setState({
|
||||||
|
meta: {
|
||||||
|
...prevMeta,
|
||||||
|
folderUid: 'new-folder-uid',
|
||||||
|
folderTitle: 'new-folder-title',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(scene.state.isDirty).toBe(true);
|
||||||
|
|
||||||
|
scene.exitEditMode({ skipConfirm: true });
|
||||||
|
expect(scene.state.meta).toEqual(prevMeta);
|
||||||
|
});
|
||||||
|
|
||||||
it('A change to refresh picker interval settings should set isDirty true', () => {
|
it('A change to refresh picker interval settings should set isDirty true', () => {
|
||||||
const refreshPicker = dashboardSceneGraph.getRefreshPicker(scene)!;
|
const refreshPicker = dashboardSceneGraph.getRefreshPicker(scene)!;
|
||||||
const prevState = [...refreshPicker.state.intervals!];
|
const prevState = [...refreshPicker.state.intervals!];
|
||||||
|
@ -101,7 +101,6 @@ export class GeneralSettingsEditView
|
|||||||
...this._dashboard.state.meta,
|
...this._dashboard.state.meta,
|
||||||
folderUid: newUID || this._dashboard.state.meta.folderUid,
|
folderUid: newUID || this._dashboard.state.meta.folderUid,
|
||||||
folderTitle: newTitle || this._dashboard.state.meta.folderTitle,
|
folderTitle: newTitle || this._dashboard.state.meta.folderTitle,
|
||||||
hasUnsavedFolderChange: true,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this._dashboard.setState({ meta: newMeta });
|
this._dashboard.setState({ meta: newMeta });
|
||||||
|
@ -13,9 +13,19 @@ interface SaveDashboardDiffProps {
|
|||||||
|
|
||||||
// calculated by parent so we can see summary in tabs
|
// calculated by parent so we can see summary in tabs
|
||||||
diff?: Diffs;
|
diff?: Diffs;
|
||||||
|
hasFolderChanges?: boolean;
|
||||||
|
oldFolder?: string;
|
||||||
|
newFolder?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SaveDashboardDiff = ({ diff, oldValue, newValue }: SaveDashboardDiffProps) => {
|
export const SaveDashboardDiff = ({
|
||||||
|
diff,
|
||||||
|
oldValue,
|
||||||
|
newValue,
|
||||||
|
hasFolderChanges,
|
||||||
|
oldFolder,
|
||||||
|
newFolder,
|
||||||
|
}: SaveDashboardDiffProps) => {
|
||||||
const loader = useAsync(async () => {
|
const loader = useAsync(async () => {
|
||||||
const oldJSON = JSON.stringify(oldValue ?? {}, null, 2);
|
const oldJSON = JSON.stringify(oldValue ?? {}, null, 2);
|
||||||
const newJSON = JSON.stringify(newValue ?? {}, null, 2);
|
const newJSON = JSON.stringify(newValue ?? {}, null, 2);
|
||||||
@ -48,23 +58,29 @@ export const SaveDashboardDiff = ({ diff, oldValue, newValue }: SaveDashboardDif
|
|||||||
}, [diff, oldValue, newValue]);
|
}, [diff, oldValue, newValue]);
|
||||||
|
|
||||||
const { value } = loader;
|
const { value } = loader;
|
||||||
if (!value || !oldValue) {
|
|
||||||
return <Spinner />;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.count < 1) {
|
|
||||||
return <div>No changes in this dashboard</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack direction="column" gap={1}>
|
<Stack direction="column" gap={1}>
|
||||||
{value.schemaChange && value.schemaChange}
|
{hasFolderChanges && (
|
||||||
{value.showDiffs && value.diffs}
|
<DiffGroup
|
||||||
|
diffs={[{ op: 'replace', value: newFolder, originalValue: oldFolder, path: [], startLineNumber: 0 }]}
|
||||||
<Box paddingTop={2}>
|
key={'folder'}
|
||||||
<h4>Full JSON diff</h4>
|
title={'folder'}
|
||||||
{value.jsonView}
|
/>
|
||||||
</Box>
|
)}
|
||||||
|
{(!value || !oldValue) && <Spinner />}
|
||||||
|
{value && value.count >= 1 ? (
|
||||||
|
<>
|
||||||
|
{value && value.schemaChange && value.schemaChange}
|
||||||
|
{value && value.showDiffs && value.diffs}
|
||||||
|
<Box paddingTop={1}>
|
||||||
|
<h4>Full JSON diff</h4>
|
||||||
|
{value.jsonView}
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<Box paddingTop={1}>No changes in the dashboard JSON</Box>
|
||||||
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user