import {Fragment, useMemo} from 'react';
import styled from '@emotion/styled';
import * as qs from 'query-string';
import {Button as CommonButton, LinkButton} from 'sentry/components/button';
import {DataSection} from 'sentry/components/events/styles';
import {Tooltip} from 'sentry/components/tooltip';
import {t, tct} from 'sentry/locale';
import {space} from 'sentry/styles/space';
import {getDuration} from 'sentry/utils/formatters';
import type {ColorOrAlias} from 'sentry/utils/theme';
const DetailContainer = styled('div')`
display: flex;
flex-direction: column;
gap: ${space(2)};
padding: ${space(1)};
${DataSection} {
padding: 0;
}
`;
const FlexBox = styled('div')`
display: flex;
align-items: center;
`;
const Actions = styled(FlexBox)`
gap: ${space(0.5)};
flex-wrap: wrap;
justify-content: end;
`;
const Title = styled(FlexBox)`
gap: ${space(1)};
flex: none;
width: 50%;
`;
const TitleText = styled('div')`
${p => p.theme.overflowEllipsis}
`;
const Type = styled('div')`
font-size: ${p => p.theme.fontSizeSmall};
`;
const TitleOp = styled('div')`
font-size: 15px;
font-weight: bold;
${p => p.theme.overflowEllipsis}
`;
const Table = styled('table')`
margin-bottom: 0 !important;
td {
overflow: hidden;
}
`;
const IconTitleWrapper = styled(FlexBox)`
gap: ${space(1)};
`;
const IconBorder = styled('div')<{backgroundColor: string; errored?: boolean}>`
background-color: ${p => p.backgroundColor};
border-radius: ${p => p.theme.borderRadius};
padding: 0;
display: flex;
align-items: center;
justify-content: center;
width: 30px;
height: 30px;
svg {
fill: ${p => p.theme.white};
width: 14px;
height: 14px;
}
`;
const Button = styled(CommonButton)`
position: absolute;
top: ${space(0.75)};
right: ${space(0.5)};
`;
const HeaderContainer = styled(Title)`
justify-content: space-between;
overflow: hidden;
width: 100%;
`;
function EventDetailsLink(props: {eventId: string; projectSlug?: string}) {
const query = useMemo(() => {
return {...qs.parse(location.search), legacy: 1};
}, []);
return (
{t('View Event Details')}
);
}
const DURATION_COMPARISON_STATUS_COLORS: {
equal: {light: ColorOrAlias; normal: ColorOrAlias};
faster: {light: ColorOrAlias; normal: ColorOrAlias};
slower: {light: ColorOrAlias; normal: ColorOrAlias};
} = {
faster: {
light: 'green100',
normal: 'green300',
},
slower: {
light: 'red100',
normal: 'red300',
},
equal: {
light: 'gray100',
normal: 'gray300',
},
};
const MIN_PCT_DURATION_DIFFERENCE = 10;
type DurationProps = {
baseline: number | undefined;
duration: number;
baseDescription?: string;
ratio?: number;
};
function Duration(props: DurationProps) {
if (typeof props.duration !== 'number' || Number.isNaN(props.duration)) {
return {t('unknown')};
}
if (props.baseline === undefined || props.baseline === 0) {
return {getDuration(props.duration, 2, true)};
}
const delta = props.duration - props.baseline;
const deltaPct = Math.round(Math.abs((delta / props.baseline) * 100));
const status = delta > 0 ? 'slower' : delta < 0 ? 'faster' : 'equal';
const formattedBaseDuration = (
{getDuration(props.baseline, 2, true)}
);
const deltaText =
status === 'equal'
? tct(`equal to the avg of [formattedBaseDuration]`, {
formattedBaseDuration,
})
: status === 'faster'
? tct(`[deltaPct] faster than the avg of [formattedBaseDuration]`, {
formattedBaseDuration,
deltaPct: `${deltaPct}%`,
})
: tct(`[deltaPct] slower than the avg of [formattedBaseDuration]`, {
formattedBaseDuration,
deltaPct: `${deltaPct}%`,
});
return (
{getDuration(props.duration, 2, true)}{' '}
{props.ratio ? `(${(props.ratio * 100).toFixed()}%)` : null}
{deltaPct >= MIN_PCT_DURATION_DIFFERENCE ? (
{deltaText}
) : null}
);
}
const DurationContainer = styled('span')`
font-weight: bold;
margin-right: ${space(1)};
`;
const Comparison = styled('span')<{status: 'faster' | 'slower' | 'equal'}>`
color: ${p => p.theme[DURATION_COMPARISON_STATUS_COLORS[p.status].normal]};
`;
const TraceDrawerComponents = {
DetailContainer,
FlexBox,
Title,
Type,
TitleOp,
HeaderContainer,
Actions,
Table,
IconTitleWrapper,
IconBorder,
EventDetailsLink,
Button,
TitleText,
Duration,
};
export {TraceDrawerComponents};