import {Fragment, useCallback, useMemo, useState} from 'react'; import styled from '@emotion/styled'; import Feature from 'sentry/components/acl/feature'; import GuideAnchor from 'sentry/components/assistant/guideAnchor'; import {Button} from 'sentry/components/button'; import HookOrDefault from 'sentry/components/hookOrDefault'; import { type Field, MetricSamplesTable, SearchableMetricSamplesTable, } from 'sentry/components/metrics/metricSamplesTable'; import {normalizeDateTimeParams} from 'sentry/components/organizations/pageFilters/parse'; import {TabList, TabPanels, Tabs} from 'sentry/components/tabs'; import {Tooltip} from 'sentry/components/tooltip'; import {t} from 'sentry/locale'; import {space} from 'sentry/styles/space'; import type {PageFilters} from 'sentry/types/core'; import type {MRI} from 'sentry/types/metrics'; import {defined} from 'sentry/utils'; import {trackAnalytics} from 'sentry/utils/analytics'; import {isCustomMetric} from 'sentry/utils/metrics'; import type {FocusedMetricsSeries, MetricsWidget} from 'sentry/utils/metrics/types'; import {isMetricsEquationWidget} from 'sentry/utils/metrics/types'; import type {MetricsSamplesResults} from 'sentry/utils/metrics/useMetricsSamples'; import useOrganization from 'sentry/utils/useOrganization'; import usePageFilters from 'sentry/utils/usePageFilters'; import {CodeLocations} from 'sentry/views/metrics/codeLocations'; import type {FocusAreaProps} from 'sentry/views/metrics/context'; import {useMetricsContext} from 'sentry/views/metrics/context'; import {extendQueryWithGroupBys} from 'sentry/views/metrics/utils'; import {generateTracesRouteWithQuery} from 'sentry/views/performance/traces/utils'; enum Tab { SAMPLES = 'samples', CODE_LOCATIONS = 'codeLocations', } export function WidgetDetails() { const { selectedWidgetIndex, widgets, focusArea, setHighlightedSampleId, setMetricsSamples, } = useMetricsContext(); const selectedWidget = widgets[selectedWidgetIndex] as MetricsWidget | undefined; const handleSampleRowHover = useCallback( (sampleId?: string) => { setHighlightedSampleId(sampleId); }, [setHighlightedSampleId] ); if (!selectedWidget || isMetricsEquationWidget(selectedWidget)) { return ; } const {mri, op, query, focusedSeries} = selectedWidget; return ( ); } interface MetricDetailsProps { focusArea?: FocusAreaProps; focusedSeries?: FocusedMetricsSeries[]; mri?: MRI; onRowHover?: (sampleId?: string) => void; op?: string; query?: string; setMetricsSamples?: React.Dispatch< React.SetStateAction['data'] | undefined> >; } export function MetricDetails({ mri, op, query, focusedSeries, onRowHover, focusArea, setMetricsSamples, }: MetricDetailsProps) { const {selection} = usePageFilters(); const organization = useOrganization(); const [selectedTab, setSelectedTab] = useState(Tab.SAMPLES); const isCodeLocationsDisabled = mri && !isCustomMetric({mri}); if (isCodeLocationsDisabled && selectedTab === Tab.CODE_LOCATIONS) { setSelectedTab(Tab.SAMPLES); } const queryWithFocusedSeries = useMemo( () => focusedSeries && extendQueryWithGroupBys( query || '', focusedSeries.map(s => s.groupBy) ), [focusedSeries, query] ); const handleTabChange = useCallback( (tab: Tab) => { if (tab === Tab.CODE_LOCATIONS) { trackAnalytics('ddm.code-locations', { organization, }); } setSelectedTab(tab); }, [organization] ); const selectionRange = focusArea?.selection?.range; const selectionDatetime = defined(selectionRange) && defined(selectionRange) && defined(selectionRange) ? ({ start: selectionRange.start, end: selectionRange.end, } as PageFilters['datetime']) : undefined; const tracesTarget = generateTracesRouteWithQuery({ orgSlug: organization.slug, metric: op && mri ? { max: selectionRange?.max, min: selectionRange?.min, op: op, query: queryWithFocusedSeries, mri, } : undefined, query: { project: selection.projects as unknown as string[], environment: selection.environments, ...normalizeDateTimeParams(selectionDatetime ?? selection.datetime), }, }); return ( {t('Span Samples')} {t('Code Location')} {t('Open in Traces')} {organization.features.includes('metrics-samples-list-search') ? ( ) : ( )} ); } const MetricSampleTableWrapper = HookOrDefault({ hookName: 'component:ddm-metrics-samples-list', defaultComponent: ({children}) => {children}, }); const TrayWrapper = styled('div')` padding-top: ${space(4)}; display: grid; grid-template-rows: auto auto 1fr; `; const ContentWrapper = styled('div')` position: relative; padding-top: ${space(2)}; `; const OpenInTracesButton = styled(Button)` margin-top: ${space(0.75)}; `; const TabsAndAction = styled('div')` display: grid; grid-template-columns: 1fr auto; gap: ${space(4)}; align-items: center; `;