import {Fragment, useCallback, useMemo} from 'react'; import styled from '@emotion/styled'; import {addErrorMessage, addSuccessMessage} from 'sentry/actionCreators/indicator'; import {openModal} from 'sentry/actionCreators/modal'; import Tag from 'sentry/components/badge/tag'; import {Button, LinkButton} from 'sentry/components/button'; import {openConfirmModal} from 'sentry/components/confirm'; import {modalCss} from 'sentry/components/featureFeedback/feedbackModal'; import {PanelTable} from 'sentry/components/panels/panelTable'; import SearchBar from 'sentry/components/searchBar'; 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 {Project} from 'sentry/types/project'; import {getReadableMetricType} from 'sentry/utils/metrics/formatters'; import useOrganization from 'sentry/utils/useOrganization'; import {MetricsExtractionRuleEditModal} from 'sentry/views/settings/projectMetrics/metricsExtractionRuleEditModal'; import { type MetricsExtractionRule, useDeleteMetricsExtractionRules, useMetricsExtractionRules, } from 'sentry/views/settings/projectMetrics/utils/api'; 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, isLoading} = useMetricsExtractionRules( organization.slug, project.slug ); const {mutate: deleteMetricsExtractionRules} = useDeleteMetricsExtractionRules( organization.slug, project.slug ); const filteredExtractionRules = useMemo(() => { return (extractionRules || []).filter(rule => rule.spanAttribute.toLowerCase().includes(query.toLowerCase()) ); }, [extractionRules, query]); const handleDelete = useCallback( (rule: MetricsExtractionRule) => { openConfirmModal({ onConfirm: () => deleteMetricsExtractionRules( {metricsExtractionRules: [rule]}, { onSuccess: () => { addSuccessMessage(t('Metric extraction rule deleted')); }, onError: () => { addErrorMessage(t('Failed to delete metric extraction rule')); }, } ), message: t('Are you sure you want to delete this extraction rule?'), confirmText: t('Delete Extraction Rule'), }); }, [deleteMetricsExtractionRules] ); const handleEdit = useCallback( (rule: MetricsExtractionRule) => { openModal( props => ( ), {modalCss} ); }, [project] ); return (
{t('Metric Extraction Rules')}
{t('Add Extraction Rule')}
); } interface RulesTableProps { extractionRules: MetricsExtractionRule[]; hasSearch: boolean; isLoading: boolean; onDelete: (rule: MetricsExtractionRule) => void; onEdit: (rule: MetricsExtractionRule) => void; } function RulesTable({ extractionRules, isLoading, onDelete, onEdit, hasSearch, }: RulesTableProps) { return ( {t('Span attribute')} , {t('Type')} , {t('Unit')} , {t('Filters')} , {t('Tags')} , {t('Actions')} , ]} emptyMessage={ hasSearch ? t('No extraction rules match the query.') : t('You have not created any extraction rules yet.') } isEmpty={extractionRules.length === 0} isLoading={isLoading} > {extractionRules .toSorted((a, b) => a?.spanAttribute?.localeCompare(b?.spanAttribute)) .map(rule => ( {rule.spanAttribute} {getReadableMetricType(rule.type)} {rule.unit} {rule.conditions.length ? ( ) : ( {t('(none)')} )} {rule.tags.length ? ( ) : ( {t('(none)')} )}