refactor(provisioning): remove auto-select logic for export

- Remove useAutoSelectUnmanagedDashboards hook
- Remove autoExport URL parameter handling
- Simplify navigation in RepositoryList to just go to dashboards page
- Users can manually select dashboards to export
This commit is contained in:
Roberto Jimenez Sanchez
2025-12-02 18:53:05 +01:00
parent 9a89918c70
commit 960d4de505
3 changed files with 3 additions and 173 deletions
@@ -1,5 +1,4 @@
import { useState, useEffect } from 'react';
import { useLocation } from 'react-router-dom-v5-compat';
import { useState } from 'react';
import { Trans, t } from '@grafana/i18n';
import { config, locationService, reportInteraction } from '@grafana/runtime';
@@ -9,7 +8,6 @@ import { ManagerKind } from 'app/features/apiserver/types';
import { BulkDeleteProvisionedResource } from 'app/features/provisioning/components/BulkActions/BulkDeleteProvisionedResource';
import { BulkExportProvisionedResource } from 'app/features/provisioning/components/BulkActions/BulkExportProvisionedResource';
import { BulkMoveProvisionedResource } from 'app/features/provisioning/components/BulkActions/BulkMoveProvisionedResource';
import { useAutoSelectUnmanagedDashboards } from 'app/features/provisioning/hooks/useAutoSelectUnmanagedDashboards';
import { useSelectionProvisioningStatus } from 'app/features/provisioning/hooks/useSelectionProvisioningStatus';
import { useSelectionUnmanagedStatus } from 'app/features/provisioning/hooks/useSelectionUnmanagedStatus';
import { useSearchStateManager } from 'app/features/search/state/SearchStateManager';
@@ -47,8 +45,6 @@ export function BrowseActions({ folderDTO }: Props) {
const [moveDashboards] = useMoveDashboardsMutation();
const [, stateManager] = useSearchStateManager();
const provisioningEnabled = config.featureToggles.provisioning;
const location = useLocation();
const selectAllUnmanagedDashboards = useAutoSelectUnmanagedDashboards();
const { hasProvisioned, hasNonProvisioned } = useSelectionProvisioningStatus(
selectedItems,
@@ -58,37 +54,6 @@ export function BrowseActions({ folderDTO }: Props) {
const isSearching = stateManager.hasSearchFilters();
// Handle autoExport URL parameter
useEffect(() => {
const searchParams = new URLSearchParams(location.search);
if (searchParams.get('autoExport') === 'true' && provisioningEnabled) {
// Remove the parameter from URL
searchParams.delete('autoExport');
const newSearch = searchParams.toString();
locationService.replace({
pathname: location.pathname,
search: newSearch ? `?${newSearch}` : '',
});
// Wait for dashboards to load, then auto-select unmanaged dashboards and open export drawer
const attemptAutoSelect = async () => {
// Try multiple times with delays to ensure dashboards are loaded
for (let i = 0; i < 5; i++) {
await selectAllUnmanagedDashboards();
await new Promise((resolve) => setTimeout(resolve, 300));
}
// Open the drawer after attempting to select
setShowBulkExportProvisionedResource(true);
};
// Start after a short delay to allow page to render
setTimeout(() => {
attemptAutoSelect();
}, 500);
}
}, [location.search, location.pathname, provisioningEnabled, selectAllUnmanagedDashboards]);
const onActionComplete = () => {
dispatch(setAllSelection({ isSelected: false, folderUID: undefined }));
@@ -25,8 +25,8 @@ export function RepositoryList({ items }: Props) {
const { instanceConnected } = checkSyncSettings(items);
const handlePushUnmanaged = () => {
// Navigate to dashboards page with autoExport parameter
locationService.push('/dashboards?autoExport=true');
// Navigate to dashboards page
locationService.push('/dashboards');
};
const getResourceCountSection = () => {
@@ -1,135 +0,0 @@
import { useCallback } from 'react';
import { config } from '@grafana/runtime';
import { ManagerKind } from 'app/features/apiserver/types';
import { isProvisionedDashboard as isProvisionedDashboardFromMeta } from 'app/features/browse-dashboards/api/isProvisioned';
import { findItem } from 'app/features/browse-dashboards/state/utils';
import { getDashboardAPI } from 'app/features/dashboard/api/dashboard_api';
import { DashboardViewItem } from 'app/features/search/types';
import { useSearchStateManager } from 'app/features/search/state/SearchStateManager';
import { useDispatch, useSelector } from 'app/types/store';
import { setItemSelectionState } from '../../browse-dashboards/state/slice';
import { BrowseDashboardsState } from '../../browse-dashboards/types';
/**
* Hook to auto-select all unmanaged dashboards when triggered
* Returns a function that can be called to select all unmanaged dashboards
*/
export function useAutoSelectUnmanagedDashboards() {
const dispatch = useDispatch();
const browseState = useSelector((state) => state.browseDashboards);
const [, stateManager] = useSearchStateManager();
const isSearching = stateManager.hasSearchFilters();
const provisioningEnabled = config.featureToggles.provisioning;
const findItemInState = useCallback(
(uid: string, state: BrowseDashboardsState) => {
const item = findItem(state.rootItems?.items || [], state.childrenByParentUID, uid);
return item ? { parentUID: item.parentUID, managedBy: item.managedBy } : undefined;
},
[]
);
const getAllDashboards = useCallback(
(state: BrowseDashboardsState): DashboardViewItem[] => {
const dashboards: DashboardViewItem[] = [];
// Helper to recursively collect dashboards from a collection
const collectDashboards = (collection: typeof state.rootItems, parentUID?: string) => {
if (!collection) {
return;
}
for (const item of collection.items) {
if (item.kind === 'dashboard') {
dashboards.push(item);
} else if (item.kind === 'folder') {
// Recursively collect from children
const children = state.childrenByParentUID[item.uid];
if (children) {
collectDashboards(children, item.uid);
}
}
}
};
// Start from root items
collectDashboards(state.rootItems);
return dashboards;
},
[]
);
const checkDashboardUnmanaged = useCallback(
async (dashboard: DashboardViewItem, state: BrowseDashboardsState): Promise<boolean> => {
if (isSearching) {
// In search mode, fetch dashboard metadata
try {
const dto = await getDashboardAPI().getDashboardDTO(dashboard.uid);
return !isProvisionedDashboardFromMeta(dto);
} catch {
return false;
}
}
// Check parent folder first
if (dashboard.parentUID) {
const parent = findItemInState(dashboard.parentUID, state);
if (parent?.managedBy === ManagerKind.Repo) {
// If parent is managed, dashboard is managed
return false;
}
}
// Check dashboard itself
const item = findItemInState(dashboard.uid, state);
return item?.managedBy !== ManagerKind.Repo;
},
[isSearching, findItemInState]
);
const selectAllUnmanagedDashboards = useCallback(async () => {
if (!provisioningEnabled) {
return;
}
// Get current state at the time of execution
const currentState = browseState;
const allDashboards = getAllDashboards(currentState);
if (allDashboards.length === 0) {
// No dashboards loaded yet, wait a bit and try again
return;
}
const unmanagedDashboards: DashboardViewItem[] = [];
// Check each dashboard to see if it's unmanaged
for (const dashboard of allDashboards) {
const isUnmanaged = await checkDashboardUnmanaged(dashboard, currentState);
if (isUnmanaged) {
unmanagedDashboards.push(dashboard);
}
}
// Select all unmanaged dashboards
for (const dashboard of unmanagedDashboards) {
dispatch(
setItemSelectionState({
item: {
kind: dashboard.kind,
uid: dashboard.uid,
parentUID: dashboard.parentUID,
managedBy: dashboard.managedBy,
},
isSelected: true,
})
);
}
}, [provisioningEnabled, browseState, getAllDashboards, checkDashboardUnmanaged, dispatch]);
return selectAllUnmanagedDashboards;
}