RestoreDashboards: Create deletion process (#89234)

* feat: add button

* feat: add modal

* refactor: move component

* feat: add backend handling

* fix: opening the modal

* feat: adjust modal content

* feat: adjust old delete modal

* feat: add text to old delete modal
pull/89508/head
Laura Benz 11 months ago committed by GitHub
parent 543e71eb28
commit 7c868e9b52
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 18
      public/app/features/browse-dashboards/api/browseDashboardsAPI.ts
  2. 12
      public/app/features/browse-dashboards/components/BrowseActions/DeleteModal.tsx
  3. 44
      public/app/features/browse-dashboards/components/PermanentlyDeleteModal.tsx
  4. 35
      public/app/features/browse-dashboards/components/RecentlyDeletedActions.tsx
  5. 2
      public/app/features/browse-dashboards/components/RestoreModal.tsx
  6. 10
      public/locales/en-US/grafana.json
  7. 10
      public/locales/pseudo-LOCALE/grafana.json

@ -57,6 +57,10 @@ interface RestoreDashboardArgs {
dashboardUID: string;
}
interface HardDeleteDashboardArgs {
dashboardUID: string;
}
function createBackendSrvBaseQuery({ baseURL }: { baseURL: string }): BaseQueryFn<RequestOptions> {
async function backendSrvBaseQuery(requestOptions: RequestOptions) {
try {
@ -382,6 +386,19 @@ export const browseDashboardsAPI = createApi({
method: 'PATCH',
}),
}),
// permanently delete a dashboard. used in PermanentlyDeleteModal.
hardDeleteDashboard: builder.mutation<void, HardDeleteDashboardArgs>({
query: ({ dashboardUID }) => ({
url: `/dashboards/uid/${dashboardUID}/trash`,
method: 'DELETE',
}),
onQueryStarted: ({ dashboardUID }, { queryFulfilled, dispatch }) => {
queryFulfilled.then(() => {
dispatch(refreshParents([dashboardUID]));
});
},
}),
}),
});
@ -397,6 +414,7 @@ export const {
useSaveDashboardMutation,
useSaveFolderMutation,
useRestoreDashboardMutation,
useHardDeleteDashboardMutation,
} = browseDashboardsAPI;
export { skipToken } from '@reduxjs/toolkit/query/react';

@ -35,6 +35,18 @@ export const DeleteModal = ({ onConfirm, onDismiss, selectedItems, ...props }: P
<ConfirmModal
body={
<>
{config.featureToggles.dashboardRestore && (
<>
<Text element="p">
<Trans i18nKey="browse-dashboards.action.delete-modal-restore-dashboards-text">
This action will delete the selected folders immediately but the selected dashboards will be marked
for deletion in 30 days. You can restore the dashboards anytime before the 30 days expires. Folders
cannot be restored.
</Trans>
</Text>
<Space v={2} />
</>
)}
<Text element="p">
<Trans i18nKey="browse-dashboards.action.delete-modal-text">
This action will delete the following content:

@ -0,0 +1,44 @@
import React from 'react';
import { ConfirmModal, Text } from '@grafana/ui';
import { Trans, t } from '../../../core/internationalization';
import { Props as ModalProps } from './RestoreModal';
export const PermanentlyDeleteModal = ({
onConfirm,
onDismiss,
selectedDashboards,
isLoading,
...props
}: ModalProps) => {
const numberOfDashboards = selectedDashboards.length;
const onDelete = async () => {
await onConfirm();
onDismiss();
};
return (
<ConfirmModal
body={
<Text element="p">
<Trans i18nKey="recently-deleted.permanently-delete-modal.text" count={numberOfDashboards}>
This action will delete {{ numberOfDashboards }} dashboards.
</Trans>
</Text>
}
title={t('recently-deleted.permanently-delete-modal.title', 'Permanently Delete Dashboards')}
confirmationText={t('recently-deleted.permanently-delete-modal.confirm-text', 'Delete')}
confirmText={
isLoading
? t('recently-deleted.permanently-delete-modal.delete-loading', 'Deleting...')
: t('recently-deleted.permanently-delete-modal.delete-button', 'Delete')
}
confirmButtonVariant="destructive"
onConfirm={onDelete}
onDismiss={onDismiss}
{...props}
/>
);
};

@ -9,10 +9,12 @@ import appEvents from '../../../core/app_events';
import { Trans } from '../../../core/internationalization';
import { useDispatch } from '../../../types';
import { ShowModalReactEvent } from '../../../types/events';
import { useRestoreDashboardMutation } from '../../browse-dashboards/api/browseDashboardsAPI';
import { clearFolders, setAllSelection, useActionSelectionState } from '../../browse-dashboards/state';
import { useHardDeleteDashboardMutation, useRestoreDashboardMutation } from '../api/browseDashboardsAPI';
import { useRecentlyDeletedStateManager } from '../api/useRecentlyDeletedStateManager';
import { RestoreModal } from '../components/RestoreModal';
import { clearFolders, setAllSelection, useActionSelectionState } from '../state';
import { PermanentlyDeleteModal } from './PermanentlyDeleteModal';
import { RestoreModal } from './RestoreModal';
export function RecentlyDeletedActions() {
const styles = useStyles2(getStyles);
@ -22,6 +24,7 @@ export function RecentlyDeletedActions() {
const [, stateManager] = useRecentlyDeletedStateManager();
const [restoreDashboard, { isLoading: isRestoreLoading }] = useRestoreDashboardMutation();
const [deleteDashboard, { isLoading: isDeleteLoading }] = useHardDeleteDashboardMutation();
const selectedDashboards = useMemo(() => {
return Object.entries(selectedItemsState.dashboard)
@ -64,6 +67,13 @@ export function RecentlyDeletedActions() {
onActionComplete();
};
const onDelete = async () => {
const promises = selectedDashboards.map((uid) => deleteDashboard({ dashboardUID: uid }));
await Promise.all(promises);
onActionComplete();
};
const showRestoreModal = () => {
appEvents.publish(
new ShowModalReactEvent({
@ -77,17 +87,36 @@ export function RecentlyDeletedActions() {
);
};
const showDeleteModal = () => {
appEvents.publish(
new ShowModalReactEvent({
component: PermanentlyDeleteModal,
props: {
selectedDashboards,
onConfirm: onDelete,
isLoading: isDeleteLoading,
},
})
);
};
return (
<div className={styles.row}>
<Button onClick={showRestoreModal} variant="secondary">
<Trans i18nKey="recently-deleted.buttons.restore">Restore</Trans>
</Button>
<Button onClick={showDeleteModal} variant="destructive">
<Trans i18nKey="recently-deleted.buttons.delete">Delete permanently</Trans>
</Button>
</div>
);
}
const getStyles = (theme: GrafanaTheme2) => ({
row: css({
display: 'flex',
flexDirection: 'row',
gap: theme.spacing(1),
marginBottom: theme.spacing(2),
}),
});

@ -4,7 +4,7 @@ import { ConfirmModal, Text } from '@grafana/ui';
import { Trans, t } from '../../../core/internationalization';
interface Props {
export interface Props {
isOpen: boolean;
onConfirm: () => Promise<void>;
onDismiss: () => void;

@ -78,6 +78,7 @@
"delete-button": "Delete",
"delete-modal-invalid-text": "One or more folders contain library panels or alert rules. Delete these first in order to proceed.",
"delete-modal-invalid-title": "Cannot delete folder",
"delete-modal-restore-dashboards-text": "This action will delete the selected folders immediately but the selected dashboards will be marked for deletion in 30 days. You can restore the dashboards anytime before the 30 days expires. Folders cannot be restored.",
"delete-modal-text": "This action will delete the following content:",
"delete-modal-title": "Delete",
"deleting": "Deleting...",
@ -1574,8 +1575,17 @@
},
"recently-deleted": {
"buttons": {
"delete": "Delete permanently",
"restore": "Restore"
},
"permanently-delete-modal": {
"confirm-text": "Delete",
"delete-button": "Delete",
"delete-loading": "Deleting...",
"text_one": "This action will delete {{numberOfDashboards}} dashboards.",
"text_other": "This action will delete {{numberOfDashboards}} dashboards.",
"title": "Permanently Delete Dashboards"
},
"restore-modal": {
"restore-button": "Restore",
"restore-loading": "Restoring...",

@ -78,6 +78,7 @@
"delete-button": "Đęľęŧę",
"delete-modal-invalid-text": "Øʼnę őř mőřę ƒőľđęřş čőʼnŧäįʼn ľįþřäřy päʼnęľş őř äľęřŧ řūľęş. Đęľęŧę ŧĥęşę ƒįřşŧ įʼn őřđęř ŧő přőčęęđ.",
"delete-modal-invalid-title": "Cäʼnʼnőŧ đęľęŧę ƒőľđęř",
"delete-modal-restore-dashboards-text": "Ŧĥįş äčŧįőʼn ŵįľľ đęľęŧę ŧĥę şęľęčŧęđ ƒőľđęřş įmmęđįäŧęľy þūŧ ŧĥę şęľęčŧęđ đäşĥþőäřđş ŵįľľ þę mäřĸęđ ƒőř đęľęŧįőʼn įʼn 30 đäyş. Ÿőū čäʼn řęşŧőřę ŧĥę đäşĥþőäřđş äʼnyŧįmę þęƒőřę ŧĥę 30 đäyş ęχpįřęş. Főľđęřş čäʼnʼnőŧ þę řęşŧőřęđ.",
"delete-modal-text": "Ŧĥįş äčŧįőʼn ŵįľľ đęľęŧę ŧĥę ƒőľľőŵįʼnģ čőʼnŧęʼnŧ:",
"delete-modal-title": "Đęľęŧę",
"deleting": "Đęľęŧįʼnģ...",
@ -1574,8 +1575,17 @@
},
"recently-deleted": {
"buttons": {
"delete": "Đęľęŧę pęřmäʼnęʼnŧľy",
"restore": "Ŗęşŧőřę"
},
"permanently-delete-modal": {
"confirm-text": "Đęľęŧę",
"delete-button": "Đęľęŧę",
"delete-loading": "Đęľęŧįʼnģ...",
"text_one": "Ŧĥįş äčŧįőʼn ŵįľľ đęľęŧę {{numberOfDashboards}} đäşĥþőäřđş.",
"text_other": "Ŧĥįş äčŧįőʼn ŵįľľ đęľęŧę {{numberOfDashboards}} đäşĥþőäřđş.",
"title": "Pęřmäʼnęʼnŧľy Đęľęŧę Đäşĥþőäřđş"
},
"restore-modal": {
"restore-button": "Ŗęşŧőřę",
"restore-loading": "Ŗęşŧőřįʼnģ...",

Loading…
Cancel
Save