mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Migration: snapshot list (#24266)
* Migration: snapshots list * chore: converted SnapshotListPage to functional component * fix: dependencies for doRemoveSnapshot
This commit is contained in:
committed by
GitHub
parent
67b4c84399
commit
702e007de4
@@ -80,12 +80,14 @@ const dashbardSlice = createSlice({
|
||||
updatePanelState(state, action.payload.panelId, { plugin: action.payload.plugin });
|
||||
},
|
||||
cleanUpEditPanel: (state, action: PayloadAction) => {
|
||||
// TODO: refactor, since the state should be mutated by copying only
|
||||
delete state.panels[EDIT_PANEL_ID];
|
||||
},
|
||||
setPanelAngularComponent: (state: DashboardState, action: PayloadAction<SetPanelAngularComponentPayload>) => {
|
||||
updatePanelState(state, action.payload.panelId, { angularComponent: action.payload.angularComponent });
|
||||
},
|
||||
addPanel: (state, action: PayloadAction<PanelModel>) => {
|
||||
// TODO: refactor, since the state should be mutated by copying only
|
||||
state.panels[action.payload.id] = { pluginId: action.payload.type };
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
import _ from 'lodash';
|
||||
import { ILocationService, IScope } from 'angular';
|
||||
import { getBackendSrv } from '@grafana/runtime';
|
||||
|
||||
import { NavModelSrv } from 'app/core/core';
|
||||
import { GrafanaRootScope } from 'app/routes/GrafanaCtrl';
|
||||
import { CoreEvents } from 'app/types';
|
||||
import { promiseToDigest } from '../../core/utils/promiseToDigest';
|
||||
|
||||
export class SnapshotListCtrl {
|
||||
navModel: any;
|
||||
snapshots: any;
|
||||
|
||||
/** @ngInject */
|
||||
constructor(
|
||||
private $rootScope: GrafanaRootScope,
|
||||
navModelSrv: NavModelSrv,
|
||||
private $location: ILocationService,
|
||||
private $scope: IScope
|
||||
) {
|
||||
this.navModel = navModelSrv.getNav('dashboards', 'snapshots', 0);
|
||||
promiseToDigest(this.$scope)(
|
||||
getBackendSrv()
|
||||
.get('/api/dashboard/snapshots')
|
||||
.then((result: any) => {
|
||||
const baseUrl = this.$location.absUrl().replace($location.url(), '');
|
||||
this.snapshots = result.map((snapshot: any) => ({
|
||||
...snapshot,
|
||||
url: snapshot.externalUrl || `${baseUrl}/dashboard/snapshot/${snapshot.key}`,
|
||||
}));
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
removeSnapshotConfirmed(snapshot: any) {
|
||||
_.remove(this.snapshots, { key: snapshot.key });
|
||||
promiseToDigest(this.$scope)(
|
||||
getBackendSrv()
|
||||
.delete('/api/snapshots/' + snapshot.key)
|
||||
.then(
|
||||
() => {},
|
||||
() => {
|
||||
this.snapshots.push(snapshot);
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
removeSnapshot(snapshot: any) {
|
||||
this.$rootScope.appEvent(CoreEvents.showConfirmModal, {
|
||||
title: 'Delete',
|
||||
text: 'Are you sure you want to delete snapshot ' + snapshot.name + '?',
|
||||
yesText: 'Delete',
|
||||
icon: 'trash-alt',
|
||||
onConfirm: () => {
|
||||
this.removeSnapshotConfirmed(snapshot);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
30
public/app/features/manage-dashboards/SnapshotListPage.tsx
Normal file
30
public/app/features/manage-dashboards/SnapshotListPage.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import React, { FC } from 'react';
|
||||
import { MapStateToProps, connect } from 'react-redux';
|
||||
import { NavModel } from '@grafana/data';
|
||||
import Page from 'app/core/components/Page/Page';
|
||||
import { getUrl } from 'app/core/selectors/location';
|
||||
import { StoreState } from 'app/types';
|
||||
import { SnapshotListTable } from './components/SnapshotListTable';
|
||||
import { getDashboardNavModel } from './state/selectors';
|
||||
|
||||
interface Props {
|
||||
navModel: NavModel;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export const SnapshotListPage: FC<Props> = ({ navModel, url }) => {
|
||||
return (
|
||||
<Page navModel={navModel}>
|
||||
<Page.Contents>
|
||||
<SnapshotListTable url={url} />
|
||||
</Page.Contents>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
|
||||
const mapStateToProps: MapStateToProps<Props, {}, StoreState> = (state: StoreState) => ({
|
||||
navModel: getDashboardNavModel(state),
|
||||
url: getUrl(state.location),
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(SnapshotListPage);
|
||||
@@ -0,0 +1,100 @@
|
||||
import React, { FC, useState, useCallback, useEffect } from 'react';
|
||||
import { ConfirmModal, Button, LinkButton } from '@grafana/ui';
|
||||
import { getBackendSrv } from '@grafana/runtime';
|
||||
import { noop } from 'rxjs';
|
||||
import { Snapshot } from '../types';
|
||||
|
||||
interface Props {
|
||||
url: string;
|
||||
}
|
||||
|
||||
export const SnapshotListTable: FC<Props> = ({ url }) => {
|
||||
const [snapshots, setSnapshots] = useState<Snapshot[]>([]);
|
||||
const [removeSnapshot, setRemoveSnapshot] = useState<Snapshot>();
|
||||
|
||||
const getSnapshots = useCallback(async () => {
|
||||
await getBackendSrv()
|
||||
.get('/api/dashboard/snapshots')
|
||||
.then((result: Snapshot[]) => {
|
||||
const absUrl = window.location.href;
|
||||
const baseUrl = absUrl.replace(url, '');
|
||||
const snapshots = result.map(snapshot => ({
|
||||
...snapshot,
|
||||
url: snapshot.externalUrl || `${baseUrl}/dashboard/snapshot/${snapshot.key}`,
|
||||
}));
|
||||
setSnapshots(snapshots);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const doRemoveSnapshot = useCallback(
|
||||
async (snapshot: Snapshot) => {
|
||||
setSnapshots(snapshots.filter(ss => ss.key !== snapshot.key));
|
||||
await getBackendSrv()
|
||||
.delete(`/api/snapshots/${snapshot.key}`)
|
||||
.then(noop, () => {
|
||||
setSnapshots(snapshots.concat(snapshot));
|
||||
});
|
||||
},
|
||||
[snapshots]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
getSnapshots();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="page-container page-body">
|
||||
<table className="filter-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<strong>Name</strong>
|
||||
</th>
|
||||
<th>
|
||||
<strong>Snapshot url</strong>
|
||||
</th>
|
||||
<th style={{ width: '70px' }}></th>
|
||||
<th style={{ width: '30px' }}></th>
|
||||
<th style={{ width: '25px' }}></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{snapshots.map((snapshot, key) => {
|
||||
return (
|
||||
<tr key={key}>
|
||||
<td>
|
||||
<a href={snapshot.url}>{snapshot.name}</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href={snapshot.url}>{snapshot.url}</a>
|
||||
</td>
|
||||
<td>{snapshot.external && <span className="query-keyword">External</span>}</td>
|
||||
<td className="text-center">
|
||||
<LinkButton href={snapshot.url} variant="secondary" size="sm" icon="eye">
|
||||
View
|
||||
</LinkButton>
|
||||
</td>
|
||||
<td className="text-right">
|
||||
<Button variant="destructive" size="sm" icon="times" onClick={() => setRemoveSnapshot(snapshot)} />
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<ConfirmModal
|
||||
isOpen={!!removeSnapshot}
|
||||
icon="trash-alt"
|
||||
title="Delete"
|
||||
body={`Are you sure you want to delete '${removeSnapshot?.name}'?`}
|
||||
confirmText="Delete"
|
||||
onDismiss={() => setRemoveSnapshot(undefined)}
|
||||
onConfirm={() => {
|
||||
doRemoveSnapshot(removeSnapshot);
|
||||
setRemoveSnapshot(undefined);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -3,10 +3,3 @@ export { ValidationSrv } from './services/ValidationSrv';
|
||||
|
||||
// Components
|
||||
export * from './components/UploadDashboard';
|
||||
|
||||
// Controllers
|
||||
import { SnapshotListCtrl } from './SnapshotListCtrl';
|
||||
|
||||
import coreModule from 'app/core/core_module';
|
||||
|
||||
coreModule.controller('SnapshotListCtrl', SnapshotListCtrl);
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
<page-header model="ctrl.navModel"></page-header>
|
||||
|
||||
<div class="page-container page-body">
|
||||
<table class="filter-table">
|
||||
<thead>
|
||||
<th><strong>Name</strong></th>
|
||||
<th><strong>Snapshot url</strong></th>
|
||||
<th style="width: 70px"></th>
|
||||
<th style="width: 30px"></th>
|
||||
<th style="width: 25px"></th>
|
||||
</thead>
|
||||
<tr ng-repeat="snapshot in ctrl.snapshots">
|
||||
<td>
|
||||
<a href="{{snapshot.url}}">{{snapshot.name}}</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{snapshot.url}}">{{snapshot.url}}</a>
|
||||
</td>
|
||||
<td>
|
||||
<span class="query-keyword" ng-if="snapshot.external">External</span>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<a href="{{snapshot.url}}" class="btn btn-inverse btn-small">
|
||||
<icon name="'eye'" style="margin-right: 4px;"></icon>
|
||||
View
|
||||
</a>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<a ng-click="ctrl.removeSnapshot(snapshot)" class="btn btn-danger btn-small">
|
||||
<icon name="'times'"></icon>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<footer />
|
||||
28
public/app/features/manage-dashboards/state/selectors.ts
Normal file
28
public/app/features/manage-dashboards/state/selectors.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { NavModel } from '@grafana/data';
|
||||
import { StoreState } from 'app/types';
|
||||
import { getUrl } from 'app/core/selectors/location';
|
||||
import { getNavModel } from 'app/core/selectors/navModel';
|
||||
|
||||
export const getDashboardNavModel = (state: StoreState): NavModel => {
|
||||
const url = getUrl(state.location);
|
||||
const navModel = getNavModel(state.navIndex, 'dashboards');
|
||||
const nav = { ...navModel };
|
||||
const node = nav.main.children?.find(item => item.url === url);
|
||||
|
||||
if (node) {
|
||||
nav.node = node;
|
||||
}
|
||||
|
||||
// This needs to be copied to avoid mutating the store in a selector
|
||||
nav.main.children = [...navModel.main.children];
|
||||
|
||||
for (const item of nav.main.children) {
|
||||
item.active = false;
|
||||
|
||||
if (item.url === nav.node.url) {
|
||||
item.active = true;
|
||||
}
|
||||
}
|
||||
|
||||
return nav;
|
||||
};
|
||||
13
public/app/features/manage-dashboards/types.ts
Normal file
13
public/app/features/manage-dashboards/types.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export interface Snapshot {
|
||||
created: string;
|
||||
expires: string;
|
||||
external: boolean;
|
||||
externalUrl: string;
|
||||
id: number;
|
||||
key: string;
|
||||
name: string;
|
||||
orgId: number;
|
||||
updated: string;
|
||||
url?: string;
|
||||
userId: number;
|
||||
}
|
||||
@@ -432,9 +432,14 @@ export function setupAngularRoutes($routeProvider: route.IRouteProvider, $locati
|
||||
pageClass: 'sidemenu-hidden',
|
||||
})
|
||||
.when('/dashboard/snapshots', {
|
||||
templateUrl: 'public/app/features/manage-dashboards/partials/snapshot_list.html',
|
||||
controller: 'SnapshotListCtrl',
|
||||
controllerAs: 'ctrl',
|
||||
template: '<react-container />',
|
||||
reloadOnSearch: false,
|
||||
resolve: {
|
||||
component: () =>
|
||||
SafeDynamicImport(
|
||||
import(/* webpackChunkName: "SnapshotListPage" */ 'app/features/manage-dashboards/SnapshotListPage')
|
||||
),
|
||||
},
|
||||
})
|
||||
.when('/plugins', {
|
||||
template: '<react-container />',
|
||||
|
||||
Reference in New Issue
Block a user