import {Fragment, useMemo} from 'react'; import styled from '@emotion/styled'; import type {Location} from 'history'; import {SectionHeading} from 'sentry/components/charts/styles'; import EventVitals from 'sentry/components/events/eventVitals'; import Panel from 'sentry/components/panels/panel'; import Placeholder from 'sentry/components/placeholder'; import {t} from 'sentry/locale'; import {space} from 'sentry/styles/space'; import type {EventTransaction, Organization} from 'sentry/types'; import {generateQueryWithTag} from 'sentry/utils'; import type EventView from 'sentry/utils/discover/eventView'; import {formatTagKey} from 'sentry/utils/discover/fields'; import type { TraceFullDetailed, TraceSplitResults, } from 'sentry/utils/performance/quickTrace/types'; import {WEB_VITAL_DETAILS} from 'sentry/utils/performance/vitals/constants'; import type {UseApiQueryResult} from 'sentry/utils/queryClient'; import type RequestError from 'sentry/utils/requestError/requestError'; import Tags from 'sentry/views/discover/tags'; import {isTraceNode} from '../../guards'; import type {TraceTree, TraceTreeNode} from '../../traceTree'; import {IssueList} from '../details/issues/issues'; const WEB_VITALS = [ WEB_VITAL_DETAILS['measurements.cls'], WEB_VITAL_DETAILS['measurements.lcp'], WEB_VITAL_DETAILS['measurements.ttfb'], WEB_VITAL_DETAILS['measurements.fcp'], WEB_VITAL_DETAILS['measurements.fid'], ]; type TraceFooterProps = { location: Location; node: TraceTreeNode; organization: Organization; rootEventResults: UseApiQueryResult; traceEventView: EventView; traces: TraceSplitResults | null; tree: TraceTree; }; function NoWebVitals() { return (
{t('WebVitals')} {WEB_VITALS.map(detail => (
{detail.name}
{' \u2014 '}
))}
); } function TraceDataLoading() { return (
{t('WebVitals')}
{t('Tag Summary')}
); } export function TraceLevelDetails(props: TraceFooterProps) { const issues = useMemo(() => { return [...props.node.errors, ...props.node.performance_issues]; }, [props.node.errors, props.node.performance_issues]); if (!(props.node && isTraceNode(props.node))) { throw new Error('Expected a trace node'); } if (!props.traces) { return ; } const {data: rootEvent} = props.rootEventResults; const webVitals = Object.keys(rootEvent?.measurements ?? {}) .filter(name => Boolean(WEB_VITAL_DETAILS[`measurements.${name}`])) .sort(); return ( {rootEvent ? ( {webVitals.length > 0 ? (
) : ( )}
{ const url = props.traceEventView.getResultsViewUrlTarget( props.organization.slug, false ); url.query = generateQueryWithTag(url.query, { key: formatTagKey(key), value, }); return url; }} totalValues={props.tree.eventsCount} eventView={props.traceEventView} organization={props.organization} location={props.location} />
) : null}
); } const Wrapper = styled('div')` display: flex; flex-direction: column; gap: ${space(2)}; `; const WebVitalsAndTags = styled('div')` display: flex; gap: ${space(2)}; `; const StyledPlaceholderTag = styled(Placeholder)` border-radius: ${p => p.theme.borderRadius}; height: 16px; margin-bottom: ${space(1.5)}; `; const StyledPlaceholderTagTitle = styled(Placeholder)` width: 100px; height: 12px; margin-bottom: ${space(0.5)}; `; const StyledPlaceholderVital = styled(StyledPlaceholderTagTitle)` width: 100%; height: 50px; margin-bottom: ${space(0.5)}; `; const StyledPanel = styled(Panel)` padding: ${space(1)} ${space(1.5)}; margin-bottom: ${space(1)}; width: 100%; `; const WebVitalsWrapper = styled('div')` display: flex; align-items: center; flex-direction: column; `;