import styled from '@emotion/styled';
import ProjectBadge from 'sentry/components/idBadge/projectBadge';
import Panel from 'sentry/components/panels/panel';
import {Tooltip} from 'sentry/components/tooltip';
import {IconFire} from 'sentry/icons';
import {t} from 'sentry/locale';
import {space} from 'sentry/styles/space';
import type {Measurement} from 'sentry/types';
import {getDuration} from 'sentry/utils/formatters';
import type {Vital} from 'sentry/utils/performance/vitals/types';
import type {IconSize} from 'sentry/utils/theme';
import useProjects from 'sentry/utils/useProjects';
import {isTransactionNode} from 'sentry/views/performance/newTraceDetails/guards';
import {TraceDrawerComponents} from 'sentry/views/performance/newTraceDetails/traceDrawer/details/styles';
import {
TRACE_MEASUREMENT_LOOKUP,
type TraceTree,
} from 'sentry/views/performance/newTraceDetails/traceTree';
interface TraceVitalsProps {
trace: TraceTree;
}
export function TraceVitals(props: TraceVitalsProps) {
const {projects} = useProjects();
const measurements = Array.from(props.trace.vitals.entries());
return (
{measurements.map(([node, vital]) => {
const op = isTransactionNode(node) ? node.value['transaction.op'] : '';
const project = projects.find(p => p.slug === node.metadata.project_slug);
return (
{vital.map((v, i) => {
return ;
})}
);
})}
);
}
const VitalsContainer = styled('div')`
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: ${space(1)};
margin-top: ${space(2)};
`;
interface EventVitalProps {
value: Measurement;
vital: TraceTree.CollectedVital;
}
function formatVitalDuration(vital: Vital, value: number) {
if (vital?.type === 'duration') {
return getDuration(value / 1000, 2, true);
}
if (vital?.type === 'integer') {
return value.toFixed(0);
}
return value.toFixed(2);
}
function EventVital(props: EventVitalProps) {
const vital = TRACE_MEASUREMENT_LOOKUP[props.vital.key];
if (!vital) {
return null;
}
const failedThreshold =
vital.poorThreshold !== undefined && props.value.value >= vital.poorThreshold;
const currentValue = formatVitalDuration(vital, props.value.value);
const thresholdValue = formatVitalDuration(vital, vital?.poorThreshold ?? 0);
return (
{vital.name ?? name}
{failedThreshold ? (
) : null}
{currentValue}
);
}
const StyledPanel = styled(Panel)<{failedThreshold: boolean}>`
padding: ${space(1)} ${space(1.5)};
margin-bottom: ${space(1)};
${p => p.failedThreshold && `border: 1px solid ${p.theme.red300};`}
`;
const ValueRow = styled('div')`
display: flex;
align-items: center;
`;
const FireIconContainer = styled('span')<{size: IconSize | string}>`
display: inline-block;
height: ${p => p.theme.iconSizes[p.size] ?? p.size};
line-height: ${p => p.theme.iconSizes[p.size] ?? p.size};
margin-right: ${space(0.5)};
color: ${p => p.theme.errorText};
`;
const Value = styled('span')<{failedThreshold: boolean}>`
font-size: ${p => p.theme.fontSizeExtraLarge};
${p => p.failedThreshold && `color: ${p.theme.errorText};`}
`;