import {Fragment} from 'react'; import styled from '@emotion/styled'; import GuideAnchor from 'sentry/components/assistant/guideAnchor'; import {CopyToClipboardButton} from 'sentry/components/copyToClipboardButton'; import Link from 'sentry/components/links/link'; import LoadingIndicator from 'sentry/components/loadingIndicator'; import {Tooltip} from 'sentry/components/tooltip'; import {IconPlay} from 'sentry/icons'; import {t, tn} from 'sentry/locale'; import {space} from 'sentry/styles/space'; import type {EventTransaction, Organization} from 'sentry/types'; import {getShortEventId} from 'sentry/utils/events'; import {getDuration} from 'sentry/utils/formatters'; import type { TraceFullDetailed, TraceMeta, TraceSplitResults, } from 'sentry/utils/performance/quickTrace/types'; import type {UseApiQueryResult} from 'sentry/utils/queryClient'; import type RequestError from 'sentry/utils/requestError/requestError'; import {normalizeUrl} from 'sentry/utils/withDomainRequired'; import {BrowserDisplay} from '../transactionDetails/eventMetas'; import {MetaData} from '../transactionDetails/styles'; import {TraceDrawerComponents} from './traceDrawer/details/styles'; import type {TraceTree} from './traceModels/traceTree'; import {isTraceNode} from './guards'; function TraceHeaderEmptyTrace() { return ( ); } type TraceHeaderProps = { metaResults: UseApiQueryResult; organization: Organization; rootEventResults: UseApiQueryResult; traceID: string | undefined; traces: TraceSplitResults | null; tree: TraceTree; }; export function TraceHeader({ metaResults, rootEventResults, traces, organization, tree, traceID, }: TraceHeaderProps) { if (traces?.transactions.length === 0 && traces.orphan_errors.length === 0) { return ; } const traceNode = tree.root.children[0]; if (!(traceNode && isTraceNode(traceNode))) { throw new Error('Expected a trace node'); } const errors = traceNode.errors.size || metaResults.data?.errors || 0; const performanceIssues = traceNode.performance_issues.size || metaResults.data?.performance_issues || 0; const errorsAndIssuesCount = errors + performanceIssues; const replay_id = rootEventResults?.data?.contexts?.replay?.replay_id; const showLoadingIndicator = (rootEventResults.isLoading && rootEventResults.fetchStatus !== 'idle') || metaResults.isLoading; return ( ) : ( rootEventResults?.data?.user?.email ?? rootEventResults?.data?.user?.name ?? '\u2014' ) } subtext={null} /> ) : rootEventResults?.data ? ( ) : ( '\u2014' ) } subtext={null} /> ) : traceID ? ( {getShortEventId(traceID)} ) : ( '\u2014' ) } subtext={null} /> {replay_id && ( {getShortEventId(replay_id)} } subtext={null} /> )} ) : metaResults.data ? ( metaResults.data.transactions + metaResults.data.errors ) : ( '\u2014' ) } subtext={null} /> 0 ? (
{tn('%s error issue', '%s error issues', errors)}
{tn( '%s performance issue', '%s performance issues', performanceIssues )}
) : null } showUnderline position="bottom" > {metaResults.isLoading ? ( ) : errorsAndIssuesCount > 0 ? ( {errorsAndIssuesCount} ) : ( '\u2014' )} } subtext={null} /> ) : traceNode.space?.[1] ? ( getDuration(traceNode.space[1] / 1000, 2, true) ) : ( '\u2014' ) } subtext={null} />
); } const FlexBox = styled('div')` display: flex; align-items: center; `; const TraceHeaderContainer = styled(FlexBox)` justify-content: space-between; background-color: ${p => p.theme.background}; padding: ${space(2)} ${space(2)} 0 ${space(2)}; `; const TraceHeaderRow = styled(FlexBox)<{textAlign: 'left' | 'right'}>` gap: ${space(2)}; text-align: ${p => p.textAlign}; `; const ReplayLinkBody = styled(FlexBox)` gap: ${space(0.25)}; `;