import {Fragment, useCallback} from 'react'; import styled from '@emotion/styled'; import {addErrorMessage, addSuccessMessage} from 'sentry/actionCreators/indicator'; import {Button} from 'sentry/components/button'; import {openConfirmModal} from 'sentry/components/confirm'; import {DateTime} from 'sentry/components/dateTime'; import UserBadge from 'sentry/components/idBadge/userBadge'; import {PanelTable} from 'sentry/components/panels/panelTable'; import SearchBar from 'sentry/components/searchBar'; import {Tooltip} from 'sentry/components/tooltip'; import {IconWarning} from 'sentry/icons'; import {IconArrow} from 'sentry/icons/iconArrow'; import {IconDelete} from 'sentry/icons/iconDelete'; import {IconEdit} from 'sentry/icons/iconEdit'; import {t} from 'sentry/locale'; import {space} from 'sentry/styles/space'; import type {MetricsExtractionRule} from 'sentry/types/metrics'; import type {Project} from 'sentry/types/project'; import {trackAnalytics} from 'sentry/utils/analytics'; import {useCardinalityLimitedMetricVolume} from 'sentry/utils/metrics/useCardinalityLimitedMetricVolume'; import {useMembers} from 'sentry/utils/useMembers'; import useOrganization from 'sentry/utils/useOrganization'; import {openExtractionRuleCreateModal} from 'sentry/views/settings/projectMetrics/metricsExtractionRuleCreateModal'; import {openExtractionRuleEditModal} from 'sentry/views/settings/projectMetrics/metricsExtractionRuleEditModal'; import { useDeleteMetricsExtractionRules, useMetricsExtractionRules, } from 'sentry/views/settings/projectMetrics/utils/useMetricsExtractionRules'; import {useSearchQueryParam} from 'sentry/views/settings/projectMetrics/utils/useSearchQueryParam'; type Props = { project: Project; }; export function MetricsExtractionRulesTable({project}: Props) { const organization = useOrganization(); const [query, setQuery] = useSearchQueryParam('query'); const {data: extractionRules, isPending: isLoadingExtractionRules} = useMetricsExtractionRules({ orgId: organization.slug, projectId: project.id, query: {query}, }); const {mutate: deleteMetricsExtractionRules} = useDeleteMetricsExtractionRules( organization.slug, project.id ); const {data: cardinality, isPending: isLoadingCardinality} = useCardinalityLimitedMetricVolume({ projects: [project.id], }); const handleDelete = useCallback( (rule: MetricsExtractionRule) => { openConfirmModal({ onConfirm: () => deleteMetricsExtractionRules( {metricsExtractionRules: [rule]}, { onSuccess: () => { addSuccessMessage(t('Metric deleted')); trackAnalytics('metrics_extractions.delete', {organization}); }, onError: () => { addErrorMessage(t('Failed to delete metric')); }, } ), message: t('Are you sure you want to delete this metric?'), confirmText: t('Delete Metric'), }); }, [deleteMetricsExtractionRules, organization] ); const handleEdit = useCallback( (rule: MetricsExtractionRule) => { openExtractionRuleEditModal({ organization, metricExtractionRule: rule, source: 'settings', }); }, [organization] ); const handleCreate = useCallback(() => { openExtractionRuleCreateModal({ organization, projectId: project.id, source: 'settings', }); }, [organization, project.id]); return (
{t('Span Metrics')}
); } interface RulesTableProps { cardinality: Record; extractionRules: MetricsExtractionRule[]; hasSearch: boolean; isLoading: boolean; onDelete: (rule: MetricsExtractionRule) => void; onEdit: (rule: MetricsExtractionRule) => void; } function RulesTable({ extractionRules, cardinality, isLoading, onDelete, onEdit, hasSearch, }: RulesTableProps) { const {members} = useMembers(); const isCardinalityLimited = (rule: MetricsExtractionRule): boolean => { const mris = rule.conditions.flatMap(condition => condition.mris); return mris.some(conditionMri => cardinality[conditionMri] > 0); }; return ( {t('Span attribute')} , {t('Created by')}, {t('Created at')} , {t('Actions')} , ]} emptyMessage={ hasSearch ? t('No metrics match the query.') : t('You have not created any span metrics yet.') } isEmpty={extractionRules.length === 0} isLoading={isLoading} > {extractionRules .toSorted((a, b) => a?.spanAttribute?.localeCompare(b?.spanAttribute)) .map(rule => { const createdByUser = members.find( member => member.id === String(rule.createdById) ); return ( {isCardinalityLimited(rule) ? ( ) : null} {rule.spanAttribute}