import {useCallback, useMemo} from 'react'; import styled from '@emotion/styled'; import * as Sentry from '@sentry/react'; import {navigateTo} from 'sentry/actionCreators/navigation'; import Feature from 'sentry/components/acl/feature'; import {Button} from 'sentry/components/button'; import ButtonBar from 'sentry/components/buttonBar'; import {DropdownMenu} from 'sentry/components/dropdownMenu'; import {CreateMetricAlertFeature} from 'sentry/components/metrics/createMetricAlertFeature'; import {getQuerySymbol} from 'sentry/components/metrics/querySymbol'; import { IconBookmark, IconDashboard, IconEllipsis, IconSettings, IconSiren, } from 'sentry/icons'; import {t} from 'sentry/locale'; import {trackAnalytics} from 'sentry/utils/analytics'; import {isCustomMeasurement} from 'sentry/utils/metrics'; import {hasCustomMetricsExtractionRules} from 'sentry/utils/metrics/features'; import {formatMRI} from 'sentry/utils/metrics/mri'; import {MetricExpressionType, type MetricsQueryWidget} from 'sentry/utils/metrics/types'; import {middleEllipsis} from 'sentry/utils/string/middleEllipsis'; import useOrganization from 'sentry/utils/useOrganization'; import useRouter from 'sentry/utils/useRouter'; import {useMetricsContext} from 'sentry/views/metrics/context'; import {getCreateAlert} from 'sentry/views/metrics/metricQueryContextMenu'; import {useCreateDashboard} from 'sentry/views/metrics/useCreateDashboard'; import {useFormulaDependencies} from 'sentry/views/metrics/utils/useFormulaDependencies'; interface Props { addCustomMetric: () => void; showCustomMetricButton: boolean; } export function PageHeaderActions({showCustomMetricButton, addCustomMetric}: Props) { const router = useRouter(); const organization = useOrganization(); const formulaDependencies = useFormulaDependencies(); const {isDefaultQuery, setDefaultQuery, widgets, showQuerySymbols, isMultiChartMode} = useMetricsContext(); const createDashboard = useCreateDashboard( widgets, formulaDependencies, isMultiChartMode ); const handleToggleDefaultQuery = useCallback(() => { if (isDefaultQuery) { Sentry.metrics.increment('ddm.remove-default-query'); trackAnalytics('ddm.remove-default-query', { organization, }); setDefaultQuery(null); } else { Sentry.metrics.increment('ddm.set-default-query'); trackAnalytics('ddm.set-default-query', { organization, }); setDefaultQuery(router.location.query); } }, [isDefaultQuery, organization, router.location.query, setDefaultQuery]); const items = useMemo( () => [ { leadingItems: [], key: 'add-dashboard', label: ( {({hasFeature}) => ( {t('Add to Dashboard')} )} ), onAction: () => { if (!organization.features.includes('dashboards-edit')) { return; } trackAnalytics('ddm.add-to-dashboard', { organization, source: 'global', }); createDashboard(); }, }, { leadingItems: [], key: 'metrics-settings', label: t('Metrics Settings'), onAction: () => navigateTo(`/settings/projects/:projectId/metrics/`, router), }, ], [createDashboard, organization, router] ); const alertItems = useMemo( () => widgets .filter( (query): query is MetricsQueryWidget => query.type === MetricExpressionType.QUERY ) .map((widget, index) => { const createAlert = getCreateAlert(organization, { query: widget.query, mri: widget.mri, groupBy: widget.groupBy, op: widget.op, }); return { leadingItems: showQuerySymbols ? [{getQuerySymbol(widget.id)}:] : [], key: `add-alert-${index}`, label: widget.mri ? `${widget.op}(${middleEllipsis(formatMRI(widget.mri), 60, /\.|-|_/)})` : t('Select a metric to create an alert'), tooltip: isCustomMeasurement({mri: widget.mri}) ? t('Custom measurements cannot be used to create alerts') : undefined, disabled: !createAlert, onAction: () => { trackAnalytics('ddm.create-alert', { organization, source: 'global', }); createAlert?.(); }, }; }), [organization, showQuerySymbols, widgets] ); return ( {showCustomMetricButton && (hasCustomMetricsExtractionRules(organization) ? ( ) : ( ))} {({hasFeature}) => alertItems.length === 1 ? ( ) : ( , }} position="bottom-end" /> ) } , }} position="bottom-end" /> ); } const AddToDashboardItem = styled('div')<{disabled: boolean}>` color: ${p => (p.disabled ? p.theme.disabled : p.theme.textColor)}; `;