@ -2,17 +2,24 @@ import { useState } from 'react';
import { useTranslate } from '@grafana/i18n' ;
import { config , locationService } from '@grafana/runtime' ;
import { Dropdown , IconButton , Menu } from '@grafana/ui' ;
import { Dropdown , Menu } from '@grafana/ui' ;
import { useDispatch } from 'app/types' ;
import { alertingFolderActionsApi } from '../../api/alertingFolderActionsApi' ;
import { shouldUsePrometheusRulesPrimary } from '../../featureToggles' ;
import { FolderBulkAction , useFolderBulkActionAbility } from '../../hooks/useAbilities' ;
import { shouldUseAlertingListViewV2 , shouldUsePrometheusRulesPrimary } from '../../featureToggles' ;
import {
AlertingAction ,
FolderBulkAction ,
useAlertingAbility ,
useFolderBulkActionAbility ,
} from '../../hooks/useAbilities' ;
import { useFolder } from '../../hooks/useFolder' ;
import { fetchAllPromAndRulerRulesAction , fetchAllPromRulesAction , fetchRulerRulesAction } from '../../state/actions' ;
import { GRAFANA_RULES_SOURCE_NAME } from '../../utils/datasource' ;
import { makeFolderLink } from '../../utils/misc' ;
import { createRelativeUrl } from '../../utils/url' ;
import MoreButton from '../MoreButton' ;
import { GrafanaRuleFolderExporter } from '../export/GrafanaRuleFolderExporter' ;
import { DeleteModal } from './DeleteModal' ;
import { PauseUnpauseActionMenuItem } from './PauseUnpauseActionMenuItem' ;
@ -20,33 +27,33 @@ interface Props {
folderUID : string ;
}
export const FolderBulk ActionsButton = ( { folderUID } : Props ) = > {
export const FolderActionsButton = ( { folderUID } : Props ) = > {
const { t } = useTranslate ( ) ;
// state
const [ isDeleteModalOpen , setIsDeleteModalOpen ] = useState ( false ) ;
const [ isExporting , setIsExporting ] = useState < boolean > ( false ) ;
// abiliti es
const [ pauseSupported , pauseAllowed ] = useFolderBulkActionAbility ( FolderBulkAction . Pause ) ;
const [ deleteSupported , deleteAllowed ] = useFolderBulkActionAbility ( FolderBulkAction . Delete ) ;
// feature toggl es
const bulkActionsEnabled = config . featureToggles . alertingBulkActionsInUI ;
const listView2Enabled = shouldUseAlertingListViewV2 ( ) ;
const canPause = pauseSupported && pauseAllowed ;
const canDelete = deleteSupported && deleteAllowed ;
const [ exportRulesSupported , exportRulesAllowed ] = useAlertingAbility ( AlertingAction . ExportGrafanaManagedRules ) ;
const canExportRules = exportRulesSupported && exportRulesAllowed ;
// mutations
const [ pauseFolder , updateState ] = alertingFolderActionsApi . endpoints . pauseFolder . useMutation ( ) ;
const [ unpauseFolder , unpauseState ] = alertingFolderActionsApi . endpoints . unpauseFolder . useMutation ( ) ;
const [ deleteGrafanaRulesFromFolder , deleteState ] =
alertingFolderActionsApi . endpoints . deleteGrafanaRulesFromFolder . useMutation ( ) ;
const folderName = useFolder ( folderUID ) . folder ? . title || 'unknown folder' ;
const listView2Enabled = config . featureToggles . alertingListViewV2 ? ? false ;
const { folder } = useFolder ( folderUID ) ;
const folderName = folder ? . title || 'unknown folder' ;
const folderUrl = makeFolderLink ( folderUID ) ;
const viewComponent = listView2Enabled ? 'list' : 'grouped' ;
// URLs
const redirectToListView = useRedirectToListView ( viewComponent ) ;
if ( ! canPause && ! canDelete ) {
if ( ! folder ) {
return null ;
}
@ -57,69 +64,33 @@ export const FolderBulkActionsButton = ({ folderUID }: Props) => {
const menuItems = (
< >
{ canPause && (
< Menu.Item
url = { folderUrl }
icon = "eye"
aria - label = { t ( 'alerting.list-view.folder-actions.view.aria-label' , 'View folder' ) }
label = { t ( 'alerting.list-view.folder-actions.view.label' , 'View folder' ) }
/ >
< BulkActions folderUID = { folderUID } onClickDelete = { setIsDeleteModalOpen } isLoading = { deleteState . isLoading } / >
{ canExportRules && (
< >
< PauseUnpauseActionMenuItem
folderUID = { folderUID }
action = "pause"
executeAction = { async ( folderUID ) = > {
await pauseFolder ( { namespace : folderUID } ) . unwrap ( ) ;
await redirectToListView ( ) ;
} }
isLoading = { updateState . isLoading }
/ >
< PauseUnpauseActionMenuItem
folderUID = { folderUID }
action = "unpause"
executeAction = { async ( folderUID ) = > {
await unpauseFolder ( { namespace : folderUID } ) . unwrap ( ) ;
await redirectToListView ( ) ;
} }
isLoading = { unpauseState . isLoading }
/ >
{ bulkActionsEnabled && < Menu.Divider / > }
< ExportFolderButton onClickExport = { ( ) = > setIsExporting ( true ) } / >
< / >
) }
{ canDelete && (
< Menu.Item
label = { t ( 'alerting.folder-bulk-actions.delete.button.label' , 'Delete all rules' ) }
icon = "trash-alt"
onClick = { ( ) = > setIsDeleteModalOpen ( true ) }
disabled = { deleteState . isLoading }
/ >
) }
{ /* @TODO re-implement */ }
{ / * { l i s t V i e w 2 E n a b l e d & & (
< >
< Menu.Divider / >
< Menu.Item
label = { t ( 'alerting.folder-bulk-actions.export.button.label' , 'Export rules' ) }
icon = "download-alt"
onClick = { ( ) = > { } }
/ >
< / >
) } * / }
< / >
) ;
return (
< >
< Dropdown placement = "bottom" overlay = { < Menu > { menuItems } < / Menu > } >
{ listView2Enabled ? (
< MoreButton
fill = "text"
size = "sm"
aria - label = { t ( 'alerting.folder-bulk-actions.more-button.title' , 'Folder actions' ) }
/ >
) : (
< IconButton
name = "ellipsis-h"
size = "sm"
aria - label = { t ( 'alerting.folder-bulk-actions.more-button.title' , 'Folder actions' ) }
tooltip = { t ( 'alerting.folder-bulk-actions.more-button.tooltip' , 'Folder actions' ) }
tooltipPlacement = "top"
/ >
) }
< MoreButton
fill = "text"
size = "sm"
aria - label = { t ( 'alerting.list-view.folder-actions.button.aria-label' , 'Folder actions' ) }
title = { t ( 'alerting.list-view.folder-actions.button.title' , 'Actions' ) }
/ >
< / Dropdown >
{ isExporting && < GrafanaRuleFolderExporter folder = { folder } onClose = { ( ) = > setIsExporting ( false ) } / > }
< DeleteModal
isOpen = { isDeleteModalOpen }
onConfirm = { onConfirmDelete }
@ -145,3 +116,92 @@ function useRedirectToListView(view: string) {
return redirectToListView ;
}
function ExportFolderButton ( { onClickExport } : { onClickExport : ( ) = > void } ) {
const { t } = useTranslate ( ) ;
return (
< Menu.Item
aria - label = { t ( 'alerting.list-view.folder-actions.export.aria-label' , 'Export rules folder' ) }
data - testid = "export-folder"
key = "export-folder"
label = { t ( 'alerting.list-view.folder-actions.export.label' , 'Export rules folder' ) }
icon = "download-alt"
onClick = { onClickExport }
/ >
) ;
}
function BulkActions ( {
folderUID ,
onClickDelete ,
isLoading ,
} : {
folderUID : string ;
onClickDelete : ( showModal : boolean ) = > void ;
isLoading : boolean ;
} ) {
const { t } = useTranslate ( ) ;
// feature toggles
const listView2Enabled = shouldUseAlertingListViewV2 ( ) ;
const bulkActionsEnabled = config . featureToggles . alertingBulkActionsInUI ;
// abilities
const [ pauseSupported , pauseAllowed ] = useFolderBulkActionAbility ( FolderBulkAction . Pause ) ;
const [ deleteSupported , deleteAllowed ] = useFolderBulkActionAbility ( FolderBulkAction . Delete ) ;
const canPause = pauseSupported && pauseAllowed ;
const canDelete = deleteSupported && deleteAllowed ;
// mutations
const [ pauseFolder , updateState ] = alertingFolderActionsApi . endpoints . pauseFolder . useMutation ( ) ;
const [ unpauseFolder , unpauseState ] = alertingFolderActionsApi . endpoints . unpauseFolder . useMutation ( ) ;
// URLs
const viewComponent = listView2Enabled ? 'list' : 'grouped' ;
const redirectToListView = useRedirectToListView ( viewComponent ) ;
if ( ! bulkActionsEnabled ) {
return null ;
}
if ( ! canPause && ! canDelete ) {
return null ;
}
return (
< >
< Menu.Divider / >
{ canPause && (
< >
< PauseUnpauseActionMenuItem
folderUID = { folderUID }
action = "pause"
executeAction = { async ( folderUID ) = > {
await pauseFolder ( { namespace : folderUID } ) . unwrap ( ) ;
await redirectToListView ( ) ;
} }
isLoading = { updateState . isLoading }
/ >
< PauseUnpauseActionMenuItem
folderUID = { folderUID }
action = "unpause"
executeAction = { async ( folderUID ) = > {
await unpauseFolder ( { namespace : folderUID } ) . unwrap ( ) ;
await redirectToListView ( ) ;
} }
isLoading = { unpauseState . isLoading }
/ >
< / >
) }
{ canDelete && (
< Menu.Item
label = { t ( 'alerting.folder-bulk-actions.delete.button.label' , 'Delete all rules' ) }
icon = "trash-alt"
onClick = { ( ) = > onClickDelete ( true ) }
disabled = { isLoading }
/ >
) }
< / >
) ;
}