Commit 702e007d by Alexander Tymchuk Committed by GitHub

Migration: snapshot list (#24266)

* Migration: snapshots list

* chore: converted SnapshotListPage to functional component

* fix: dependencies for doRemoveSnapshot
parent 67b4c843
......@@ -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 };
},
},
......
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);
},
});
}
}
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);
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);
<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 />
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;
};
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 />',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment