mirror of
https://github.com/grafana/grafana.git
synced 2025-08-02 04:19:25 +08:00

* feat: hide snapshots menu item from viewers * feat(playlists): prevent viewers from creating/editing playlists * feat: prevent viewers seeing playlist nav link if no playlists * refactor(playlist): rename isViewer property to canEditPlaylists * revert(playlists): put back note if viewer and no playlists * refactor(snapshots): consider admin/editor permission in folders/dashboards for displaying menu item * feat(snapshots): only show snapshot nav item if user is signed in * fix(snapshots): revert snapshots to previous state if delete snapshot api error
101 lines
3.1 KiB
TypeScript
101 lines
3.1 KiB
TypeScript
import React, { FC, useState, useCallback, useEffect } from 'react';
|
|
import { ConfirmModal, Button, LinkButton } from '@grafana/ui';
|
|
import { getBackendSrv } from '@grafana/runtime';
|
|
import { Snapshot } from '../types';
|
|
|
|
interface Props {
|
|
url: string;
|
|
}
|
|
|
|
export const SnapshotListTable: FC<Props> = ({ url }) => {
|
|
const [snapshots, setSnapshots] = useState<Snapshot[]>([]);
|
|
const [removeSnapshot, setRemoveSnapshot] = useState<Snapshot | undefined>();
|
|
|
|
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) => {
|
|
const filteredSnapshots = snapshots.filter(ss => ss.key !== snapshot.key);
|
|
setSnapshots(filteredSnapshots);
|
|
await getBackendSrv()
|
|
.delete(`/api/snapshots/${snapshot.key}`)
|
|
.catch(() => {
|
|
setSnapshots(snapshots);
|
|
});
|
|
},
|
|
[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 => {
|
|
return (
|
|
<tr key={snapshot.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>
|
|
);
|
|
};
|