2025-04-01 10:38:02 +09:00

121 lines
3.6 KiB
TypeScript

import { useMemo } from 'react';
import { config, reportInteraction } from '@grafana/runtime';
import { Button, Stack, Tooltip } from '@grafana/ui';
import appEvents from 'app/core/app_events';
import { t, Trans } from 'app/core/internationalization';
import { useSearchStateManager } from 'app/features/search/state/SearchStateManager';
import { useDispatch } from 'app/types';
import { ShowModalReactEvent } from 'app/types/events';
import { useDeleteItemsMutation, useMoveItemsMutation } from '../../api/browseDashboardsAPI';
import { setAllSelection, useActionSelectionState } from '../../state';
import { DashboardTreeSelection } from '../../types';
import { DeleteModal } from './DeleteModal';
import { MoveModal } from './MoveModal';
export interface Props {}
export function BrowseActions() {
const dispatch = useDispatch();
const selectedItems = useActionSelectionState();
const [deleteItems] = useDeleteItemsMutation();
const [moveItems] = useMoveItemsMutation();
const [, stateManager] = useSearchStateManager();
// Folders can only be moved if nested folders is enabled
const moveIsInvalid = useMemo(
() => !config.featureToggles.nestedFolders && Object.values(selectedItems.folder).some((v) => v),
[selectedItems]
);
const isSearching = stateManager.hasSearchFilters();
const onActionComplete = () => {
dispatch(setAllSelection({ isSelected: false, folderUID: undefined }));
if (isSearching) {
// Redo search query
stateManager.doSearchWithDebounce();
}
};
const onDelete = async () => {
await deleteItems({ selectedItems });
trackAction('delete', selectedItems);
onActionComplete();
};
const onMove = async (destinationUID: string) => {
await moveItems({ selectedItems, destinationUID });
trackAction('move', selectedItems);
onActionComplete();
};
const showMoveModal = () => {
appEvents.publish(
new ShowModalReactEvent({
component: MoveModal,
props: {
selectedItems,
onConfirm: onMove,
},
})
);
};
const showDeleteModal = () => {
appEvents.publish(
new ShowModalReactEvent({
component: DeleteModal,
props: {
selectedItems,
onConfirm: onDelete,
},
})
);
};
const moveButton = (
<Button onClick={showMoveModal} variant="secondary" disabled={moveIsInvalid}>
<Trans i18nKey="browse-dashboards.action.move-button">Move</Trans>
</Button>
);
return (
<Stack gap={1} data-testid="manage-actions">
{moveIsInvalid ? (
<Tooltip content={t('browse-dashboards.action.cannot-move-folders', 'Folders cannot be moved')}>
{moveButton}
</Tooltip>
) : (
moveButton
)}
<Button onClick={showDeleteModal} variant="destructive">
<Trans i18nKey="browse-dashboards.action.delete-button">Delete</Trans>
</Button>
</Stack>
);
}
const actionMap = {
move: 'grafana_manage_dashboards_item_moved',
delete: 'grafana_manage_dashboards_item_deleted',
} as const;
function trackAction(action: keyof typeof actionMap, selectedItems: Omit<DashboardTreeSelection, 'panel' | '$all'>) {
const selectedDashboards = Object.keys(selectedItems.dashboard).filter((uid) => selectedItems.dashboard[uid]);
const selectedFolders = Object.keys(selectedItems.folder).filter((uid) => selectedItems.folder[uid]);
reportInteraction(actionMap[action], {
item_counts: {
folder: selectedFolders.length,
dashboard: selectedDashboards.length,
},
source: 'tree_actions',
restore_enabled: Boolean(config.featureToggles.dashboardRestore),
});
}