+ The new rule will NOT be marked as a provisioned rule. +
++ You will need to set a new evaluation group for the copied rule because the original one has been + provisioned and cannot be used for rules created in the UI. +
+ + } + confirmText="Copy" + onConfirm={() => setStage('redirect')} + onDismiss={onDismiss} + /> + ); +} + +interface CloneRuleButtonProps { + ruleIdentifier: RuleIdentifier; + isProvisioned: boolean; + text?: string; + className?: string; +} + +export const CloneRuleButton = React.forwardRef- The new rule will NOT be marked as a provisioned rule. -
-- You will need to set a new alert group for the copied rule because the original one has been provisioned - and cannot be used for rules created in the UI. -
- - } - confirmText="Copy" - onConfirm={() => { - locationService.push(cloneUrl); - }} - onDismiss={() => setShowModal(false)} - /> - > - ); - } -); - -CloneRuleButton.displayName = 'CloneRuleButton'; - -const getStyles = (theme: GrafanaTheme2) => ({ - bold: css` - font-weight: ${theme.typography.fontWeightBold}; - `, -}); diff --git a/public/app/features/alerting/unified/components/rules/RuleActionsButtons.tsx b/public/app/features/alerting/unified/components/rules/RuleActionsButtons.tsx index 3044a988851..1867dfc929f 100644 --- a/public/app/features/alerting/unified/components/rules/RuleActionsButtons.tsx +++ b/public/app/features/alerting/unified/components/rules/RuleActionsButtons.tsx @@ -1,23 +1,39 @@ import { css } from '@emotion/css'; +import { uniqueId } from 'lodash'; import React, { useState } from 'react'; import { useLocation } from 'react-router-dom'; +import { useToggle } from 'react-use'; import { GrafanaTheme2 } from '@grafana/data'; import { Stack } from '@grafana/experimental'; -import { Button, ClipboardButton, ConfirmModal, LinkButton, Tooltip, useStyles2 } from '@grafana/ui'; +import { + Button, + ClipboardButton, + ConfirmModal, + Dropdown, + Icon, + LinkButton, + Menu, + Tooltip, + useStyles2, +} from '@grafana/ui'; import { useAppNotification } from 'app/core/copy/appNotification'; import { useDispatch } from 'app/types'; -import { CombinedRule, RulesSource } from 'app/types/unified-alerting'; +import { CombinedRule, RuleIdentifier, RulesSource } from 'app/types/unified-alerting'; +import { contextSrv } from '../../../../../core/services/context_srv'; import { useIsRuleEditable } from '../../hooks/useIsRuleEditable'; import { deleteRuleAction } from '../../state/actions'; +import { provisioningPermissions } from '../../utils/access-control'; import { getRulesSourceName } from '../../utils/datasource'; import { createShareLink, createViewLink } from '../../utils/misc'; import * as ruleId from '../../utils/rule-id'; import { isFederatedRuleGroup, isGrafanaRulerRule } from '../../utils/rules'; import { createUrl } from '../../utils/url'; +import { GrafanaRuleExporter } from '../export/GrafanaRuleExporter'; + +import { RedirectToCloneRule } from './CloneRule'; -import { CloneRuleButton } from './CloneRuleButton'; export const matchesWidth = (width: number) => window.matchMedia(`(max-width: ${width}px)`).matches; interface Props { @@ -30,14 +46,22 @@ export const RuleActionsButtons = ({ rule, rulesSource }: Props) => { const location = useLocation(); const notifyApp = useAppNotification(); const style = useStyles2(getStyles); + + const [redirectToClone, setRedirectToClone] = useState< + { identifier: RuleIdentifier; isProvisioned: boolean } | undefined + >(undefined); + const [showExportDrawer, toggleShowExportDrawer] = useToggle(false); + const { namespace, group, rulerRule } = rule; const [ruleToDelete, setRuleToDelete] = useState