Restore dashboards: Re-enable FE feature toggle (#105820)
* Add dashboardRestore toggle * Restore the toggle on FE * Add navtree item * Fix lint * Rename feature toggle * Use the renamed toggle
This commit is contained in:
@@ -1028,4 +1028,9 @@ export interface FeatureToggles {
|
||||
* Use proxy-based read-only objects for plugin extensions instead of deep cloning
|
||||
*/
|
||||
extensionsReadOnlyProxy?: boolean;
|
||||
/**
|
||||
* Enables restore deleted dashboards feature
|
||||
* @default false
|
||||
*/
|
||||
restoreDashboards?: boolean;
|
||||
}
|
||||
|
||||
@@ -1767,6 +1767,14 @@ var (
|
||||
HideFromDocs: true,
|
||||
FrontendOnly: true,
|
||||
},
|
||||
{
|
||||
Name: "restoreDashboards",
|
||||
Description: "Enables restore deleted dashboards feature",
|
||||
Stage: FeatureStageExperimental,
|
||||
Owner: grafanaFrontendPlatformSquad,
|
||||
HideFromAdminPage: true,
|
||||
Expression: "false",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -231,3 +231,4 @@ alertingListViewV2PreviewToggle,privatePreview,@grafana/alerting-squad,false,fal
|
||||
alertRuleUseFiredAtForStartsAt,experimental,@grafana/alerting-squad,false,false,false
|
||||
alertingBulkActionsInUI,GA,@grafana/alerting-squad,false,false,true
|
||||
extensionsReadOnlyProxy,experimental,@grafana/plugins-platform-backend,false,false,true
|
||||
restoreDashboards,experimental,@grafana/grafana-frontend-platform,false,false,false
|
||||
|
||||
|
@@ -934,4 +934,8 @@ const (
|
||||
// FlagExtensionsReadOnlyProxy
|
||||
// Use proxy-based read-only objects for plugin extensions instead of deep cloning
|
||||
FlagExtensionsReadOnlyProxy = "extensionsReadOnlyProxy"
|
||||
|
||||
// FlagRestoreDashboards
|
||||
// Enables restore deleted dashboards feature
|
||||
FlagRestoreDashboards = "restoreDashboards"
|
||||
)
|
||||
|
||||
@@ -777,6 +777,21 @@
|
||||
"frontend": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"name": "dashboardRestore",
|
||||
"resourceVersion": "1747744953711",
|
||||
"creationTimestamp": "2024-05-16T17:36:26Z",
|
||||
"deletionTimestamp": "2025-04-03T07:52:54Z"
|
||||
},
|
||||
"spec": {
|
||||
"description": "Enables deleted dashboard restore feature",
|
||||
"stage": "experimental",
|
||||
"codeowner": "@grafana/grafana-frontend-platform",
|
||||
"hideFromAdminPage": true,
|
||||
"expression": "false"
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"name": "dashboardScene",
|
||||
@@ -2897,6 +2912,20 @@
|
||||
"expression": "true"
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"name": "restoreDashboards",
|
||||
"resourceVersion": "1748002635285",
|
||||
"creationTimestamp": "2025-05-23T12:17:15Z"
|
||||
},
|
||||
"spec": {
|
||||
"description": "Enables restore deleted dashboards feature",
|
||||
"stage": "experimental",
|
||||
"codeowner": "@grafana/grafana-frontend-platform",
|
||||
"hideFromAdminPage": true,
|
||||
"expression": "false"
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"name": "rolePickerDrawer",
|
||||
|
||||
@@ -399,6 +399,15 @@ func (s *ServiceImpl) buildDashboardNavLinks(c *contextmodel.ReqContext) []*navt
|
||||
Icon: "library-panel",
|
||||
})
|
||||
}
|
||||
|
||||
if s.features.IsEnabled(c.Req.Context(), featuremgmt.FlagRestoreDashboards) && (c.GetOrgRole() == org.RoleAdmin || c.IsGrafanaAdmin) {
|
||||
dashboardChildNavs = append(dashboardChildNavs, &navtree.NavLink{
|
||||
Text: "Recently deleted",
|
||||
SubTitle: "Any items listed here for more than 30 days will be automatically deleted.",
|
||||
Id: "dashboards/recently-deleted",
|
||||
Url: s.cfg.AppSubURL + "/dashboard/recently-deleted",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if hasAccess(ac.EvalPermission(dashboards.ActionDashboardsCreate)) {
|
||||
|
||||
@@ -6,7 +6,7 @@ import AutoSizer from 'react-virtualized-auto-sizer';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { Trans } from '@grafana/i18n';
|
||||
import { reportInteraction } from '@grafana/runtime';
|
||||
import { config, reportInteraction } from '@grafana/runtime';
|
||||
import { LinkButton, FilterInput, useStyles2, Text, Stack } from '@grafana/ui';
|
||||
import { Page } from 'app/core/components/Page/Page';
|
||||
import { getConfig } from 'app/core/config';
|
||||
@@ -137,16 +137,15 @@ const BrowseDashboardsPage = memo(() => {
|
||||
renderTitle={renderTitle}
|
||||
actions={
|
||||
<>
|
||||
{false &&
|
||||
hasAdminRights && ( // TODO: change this to a feature flag when dashboard restore is reworked
|
||||
<LinkButton
|
||||
variant="secondary"
|
||||
href={getConfig().appSubUrl + '/dashboard/recently-deleted'}
|
||||
onClick={handleButtonClickToRecentlyDeleted}
|
||||
>
|
||||
<Trans i18nKey="browse-dashboards.actions.button-to-recently-deleted">Recently deleted</Trans>
|
||||
</LinkButton>
|
||||
)}
|
||||
{config.featureToggles.restoreDashboards && hasAdminRights && (
|
||||
<LinkButton
|
||||
variant="secondary"
|
||||
href={getConfig().appSubUrl + '/dashboard/recently-deleted'}
|
||||
onClick={handleButtonClickToRecentlyDeleted}
|
||||
>
|
||||
<Trans i18nKey="browse-dashboards.actions.button-to-recently-deleted">Recently deleted</Trans>
|
||||
</LinkButton>
|
||||
)}
|
||||
{folderDTO && <FolderActionsButton folder={folderDTO} />}
|
||||
{(canCreateDashboards || canCreateFolders) && (
|
||||
<CreateNewButton
|
||||
|
||||
@@ -116,6 +116,6 @@ function trackAction(action: keyof typeof actionMap, selectedItems: Omit<Dashboa
|
||||
dashboard: selectedDashboards.length,
|
||||
},
|
||||
source: 'tree_actions',
|
||||
restore_enabled: false,
|
||||
restore_enabled: Boolean(config.featureToggles.restoreDashboards),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
import { Trans, useTranslate } from '@grafana/i18n';
|
||||
import { reportInteraction } from '@grafana/runtime';
|
||||
import { config, reportInteraction } from '@grafana/runtime';
|
||||
import { Alert, ConfirmModal, Text, Space } from '@grafana/ui';
|
||||
|
||||
import { useGetAffectedItemsQuery } from '../../api/browseDashboardsAPI';
|
||||
@@ -44,7 +44,7 @@ export const DeleteModal = ({ onConfirm, onDismiss, selectedItems, ...props }: P
|
||||
<ConfirmModal
|
||||
body={
|
||||
<>
|
||||
{false && ( // TODO: change this to a feature flag when dashboard restore is reworked
|
||||
{config.featureToggles.restoreDashboards && (
|
||||
<>
|
||||
<Text element="p">
|
||||
<Trans i18nKey="browse-dashboards.action.delete-modal-restore-dashboards-text">
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as React from 'react';
|
||||
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { Trans, useTranslate } from '@grafana/i18n';
|
||||
import { isFetchError } from '@grafana/runtime';
|
||||
import { config, isFetchError } from '@grafana/runtime';
|
||||
import { Dashboard } from '@grafana/schema';
|
||||
import { Spec as DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha1/types.spec.gen';
|
||||
import { Alert, Box, Button, Stack } from '@grafana/ui';
|
||||
@@ -42,7 +42,7 @@ export interface NameAlreadyExistsErrorProps {
|
||||
|
||||
export function NameAlreadyExistsError({ cancelButton, saveButton }: NameAlreadyExistsErrorProps) {
|
||||
const { t } = useTranslate();
|
||||
const isRestoreDashboardsEnabled = false;
|
||||
const isRestoreDashboardsEnabled = config.featureToggles.restoreDashboards;
|
||||
return isRestoreDashboardsEnabled ? (
|
||||
<Alert title={t('save-dashboards.name-exists.title', 'Dashboard name already exists')} severity="error">
|
||||
<p>
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useAsyncFn, useToggle } from 'react-use';
|
||||
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { Trans, useTranslate } from '@grafana/i18n';
|
||||
import { reportInteraction } from '@grafana/runtime';
|
||||
import { config, reportInteraction } from '@grafana/runtime';
|
||||
import { Button, ConfirmModal, Modal, Space, Text, TextLink } from '@grafana/ui';
|
||||
|
||||
import { useDeleteItemsMutation } from '../../browse-dashboards/api/browseDashboardsAPI';
|
||||
@@ -34,7 +34,7 @@ export function DeleteDashboardButton({ dashboard }: ButtonProps) {
|
||||
dashboard: 1,
|
||||
},
|
||||
source: 'dashboard_scene_settings',
|
||||
restore_enabled: false,
|
||||
restore_enabled: Boolean(config.featureToggles.restoreDashboards),
|
||||
});
|
||||
toggleModal();
|
||||
if (dashboard.state.uid) {
|
||||
@@ -83,7 +83,7 @@ export function DeleteDashboardModal({ dashboardTitle, onConfirm, onClose }: Del
|
||||
isOpen={true}
|
||||
body={
|
||||
<>
|
||||
{false && ( // TODO: re-enable when restore is reworked
|
||||
{config.featureToggles.restoreDashboards && (
|
||||
<>
|
||||
<Text element="p">
|
||||
<Trans i18nKey="dashboard-settings.delete-modal-restore-dashboards-text">
|
||||
|
||||
@@ -3,7 +3,7 @@ import { connect, ConnectedProps } from 'react-redux';
|
||||
import useAsyncFn from 'react-use/lib/useAsyncFn';
|
||||
|
||||
import { Trans, useTranslate } from '@grafana/i18n';
|
||||
import { locationService, reportInteraction } from '@grafana/runtime';
|
||||
import { config, locationService, reportInteraction } from '@grafana/runtime';
|
||||
import { Modal, Button, Text, Space, TextLink } from '@grafana/ui';
|
||||
import { DashboardModel } from 'app/features/dashboard/state/DashboardModel';
|
||||
import { cleanUpDashboardAndVariables } from 'app/features/dashboard/state/actions';
|
||||
@@ -34,7 +34,7 @@ const DeleteDashboardModalUnconnected = ({ hideModal, cleanUpDashboardAndVariabl
|
||||
dashboard: 1,
|
||||
},
|
||||
source: 'dashboard_settings',
|
||||
restore_enabled: false,
|
||||
restore_enabled: Boolean(config.featureToggles.restoreDashboards),
|
||||
});
|
||||
await deleteItems({
|
||||
selectedItems: {
|
||||
|
||||
@@ -3,7 +3,7 @@ import * as React from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { Trans, useTranslate } from '@grafana/i18n';
|
||||
import { FetchError } from '@grafana/runtime';
|
||||
import { config, FetchError } from '@grafana/runtime';
|
||||
import { Dashboard } from '@grafana/schema';
|
||||
import { Button, ConfirmModal, Modal, useStyles2 } from '@grafana/ui';
|
||||
|
||||
@@ -32,7 +32,7 @@ export const SaveDashboardErrorProxy = ({
|
||||
}: SaveDashboardErrorProxyProps) => {
|
||||
const { onDashboardSave } = useDashboardSave();
|
||||
const { t } = useTranslate();
|
||||
const isRestoreDashboardsEnabled = false;
|
||||
const isRestoreDashboardsEnabled = config.featureToggles.restoreDashboards;
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -527,6 +527,13 @@ export function getAppRoutes(): RouteDescriptor[] {
|
||||
() => import(/* webpackChunkName: "BookmarksPage"*/ 'app/features/bookmarks/BookmarksPage')
|
||||
),
|
||||
},
|
||||
config.featureToggles.restoreDashboards && {
|
||||
path: '/dashboard/recently-deleted',
|
||||
roles: () => ['Admin', 'ServerAdmin'],
|
||||
component: SafeDynamicImport(
|
||||
() => import(/* webpackChunkName: "RecentlyDeletedPage" */ 'app/features/browse-dashboards/RecentlyDeletedPage')
|
||||
),
|
||||
},
|
||||
{
|
||||
// Redirect the /femt dev page to the root
|
||||
path: '/femt',
|
||||
|
||||
Reference in New Issue
Block a user