import {Fragment, useMemo} from 'react';
import {useTheme} from '@emotion/react';
import moment from 'moment-timezone';
import Count from 'sentry/components/count';
import {EmptyStreamWrapper} from 'sentry/components/emptyStateWarning';
import LoadingIndicator from 'sentry/components/loadingIndicator';
import PerformanceDuration from 'sentry/components/performanceDuration';
import {IconWarning} from 'sentry/icons/iconWarning';
import {t, tct} from 'sentry/locale';
import type {Organization} from 'sentry/types/organization';
import {trackAnalytics} from 'sentry/utils/analytics';
import {getUtcDateString} from 'sentry/utils/dates';
import useOrganization from 'sentry/utils/useOrganization';
import {useDataset} from 'sentry/views/explore/hooks/useDataset';
import type {TraceResult} from 'sentry/views/explore/hooks/useTraces';
import {type SpanResult, useTraceSpans} from 'sentry/views/explore/hooks/useTraceSpans';
import {useUserQuery} from 'sentry/views/explore/hooks/useUserQuery';
import {type Field, FIELDS, SORTS} from 'sentry/views/explore/tables/tracesTable/data';
import {
SpanBreakdownSliceRenderer,
SpanDescriptionRenderer,
SpanIdRenderer,
SpanTimeRenderer,
TraceBreakdownContainer,
} from 'sentry/views/explore/tables/tracesTable/fieldRenderers';
import {
MoreMatchingSpans,
SpanPanelContent,
SpanTablePanelItem,
StyledPanel,
StyledPanelHeader,
StyledPanelItem,
StyledSpanPanelItem,
} from 'sentry/views/explore/tables/tracesTable/styles';
import {
getSecondaryNameFromSpan,
getStylingSliceName,
} from 'sentry/views/explore/tables/tracesTable/utils';
const ONE_MINUTE = 60 * 1000; // in milliseconds
export function SpanTable({
trace,
setHighlightedSliceName,
}: {
setHighlightedSliceName: (sliceName: string) => void;
trace: TraceResult;
}) {
const organization = useOrganization();
const [dataset] = useDataset();
const [query] = useUserQuery();
const {data, isPending, isError} = useTraceSpans({
dataset,
trace,
fields: [
...FIELDS,
...SORTS.map(field =>
field.startsWith('-') ? (field.substring(1) as Field) : (field as Field)
),
],
datetime: {
// give a 1 minute buffer on each side so that start != end
start: getUtcDateString(moment(trace.start - ONE_MINUTE)),
end: getUtcDateString(moment(trace.end + ONE_MINUTE)),
period: null,
utc: true,
},
limit: 10,
query,
sort: SORTS,
});
const spans = useMemo(() => data?.data ?? [], [data]);
const showErrorState = useMemo(() => {
return !isPending && isError;
}, [isPending, isError]);
const hasData = useMemo(() => {
return !isPending && !showErrorState && spans.length > 0;
}, [spans, isPending, showErrorState]);
return (
{t('Span ID')}
{t('Span Description')}
{t('Span Duration')}
{t('Timestamp')}
{isPending && (
)}
{isError && ( // TODO: need an error state
)}
{data?.data.map(span => (
))}
{hasData && spans.length < trace.matchingSpans && (
{tct('[more][space]more [matching]spans can be found in the trace.', {
more: ,
space: ,
matching: query ? 'matching ' : '',
})}
)}
);
}
function SpanRow({
organization,
span,
trace,
setHighlightedSliceName,
}: {
organization: Organization;
setHighlightedSliceName: (sliceName: string) => void;
span: SpanResult;
trace: TraceResult;
}) {
const theme = useTheme();
return (
trackAnalytics('trace_explorer.open_trace_span', {
organization,
})
}
/>
setHighlightedSliceName('')}>
setHighlightedSliceName(
getStylingSliceName(span.project, getSecondaryNameFromSpan(span)) ?? ''
)
}
/>
);
}