mirror of
https://github.com/hanzoai/sentinel.git
synced 2026-10-30 14:49:14 +00:00
308 lines
10 KiB
TypeScript
308 lines
10 KiB
TypeScript
import {browserHistory} from 'react-router';
|
|
import styled from '@emotion/styled';
|
|
import type {Location} from 'history';
|
|
|
|
import OptionSelector from 'sentry/components/charts/optionSelector';
|
|
import {
|
|
ChartContainer,
|
|
ChartControls,
|
|
InlineContainer,
|
|
} from 'sentry/components/charts/styles';
|
|
import Panel from 'sentry/components/panels/panel';
|
|
import {t} from 'sentry/locale';
|
|
import type {Organization, Project, SelectValue} from 'sentry/types';
|
|
import {trackAnalytics} from 'sentry/utils/analytics';
|
|
import type EventView from 'sentry/utils/discover/eventView';
|
|
import {useMetricsCardinalityContext} from 'sentry/utils/performance/contexts/metricsCardinality';
|
|
import {useMEPSettingContext} from 'sentry/utils/performance/contexts/metricsEnhancedSetting';
|
|
import {removeHistogramQueryStrings} from 'sentry/utils/performance/histogram';
|
|
import {decodeScalar} from 'sentry/utils/queryString';
|
|
import {getTransactionMEPParamsIfApplicable} from 'sentry/views/performance/transactionSummary/transactionOverview/utils';
|
|
import {DisplayModes} from 'sentry/views/performance/transactionSummary/utils';
|
|
import {TransactionsListOption} from 'sentry/views/releases/detail/overview';
|
|
|
|
import {
|
|
TrendFunctionField,
|
|
TrendParameterColumn,
|
|
TrendParameterLabel,
|
|
} from '../../trends/types';
|
|
import {TRENDS_FUNCTIONS, TRENDS_PARAMETERS} from '../../trends/utils';
|
|
import {SpanOperationBreakdownFilter} from '../filter';
|
|
|
|
import LatencyChartControls from './latencyChart/chartControls';
|
|
import {ZOOM_END, ZOOM_START} from './latencyChart/utils';
|
|
import DurationChart from './durationChart';
|
|
import DurationPercentileChart from './durationPercentileChart';
|
|
import LatencyChart from './latencyChart';
|
|
import TrendChart from './trendChart';
|
|
import UserMiseryChart from './userMiseryChart';
|
|
import VitalsChart from './vitalsChart';
|
|
|
|
function generateDisplayOptions(
|
|
currentFilter: SpanOperationBreakdownFilter
|
|
): SelectValue<string>[] {
|
|
if (currentFilter === SpanOperationBreakdownFilter.NONE) {
|
|
return [
|
|
{value: DisplayModes.DURATION, label: t('Duration Breakdown')},
|
|
{value: DisplayModes.DURATION_PERCENTILE, label: t('Duration Percentiles')},
|
|
{value: DisplayModes.LATENCY, label: t('Duration Distribution')},
|
|
{value: DisplayModes.TREND, label: t('Trends')},
|
|
{value: DisplayModes.VITALS, label: t('Web Vitals')},
|
|
{value: DisplayModes.USER_MISERY, label: t('User Misery')},
|
|
];
|
|
}
|
|
|
|
// A span operation name breakdown has been chosen.
|
|
|
|
return [
|
|
{value: DisplayModes.DURATION, label: t('Span Operation Breakdown')},
|
|
{value: DisplayModes.DURATION_PERCENTILE, label: t('Span Operation Percentiles')},
|
|
{value: DisplayModes.LATENCY, label: t('Span Operation Distribution')},
|
|
{value: DisplayModes.TREND, label: t('Trends')},
|
|
{value: DisplayModes.VITALS, label: t('Web Vitals')},
|
|
];
|
|
}
|
|
|
|
const TREND_FUNCTIONS_OPTIONS: SelectValue<string>[] = TRENDS_FUNCTIONS.map(
|
|
({field, label}) => ({
|
|
value: field,
|
|
label,
|
|
})
|
|
);
|
|
|
|
type Props = {
|
|
currentFilter: SpanOperationBreakdownFilter;
|
|
eventView: EventView;
|
|
location: Location;
|
|
organization: Organization;
|
|
totalValue: number | null;
|
|
withoutZerofill: boolean;
|
|
project?: Project;
|
|
};
|
|
|
|
function TransactionSummaryCharts({
|
|
totalValue,
|
|
eventView,
|
|
organization,
|
|
location,
|
|
currentFilter,
|
|
withoutZerofill,
|
|
project,
|
|
}: Props) {
|
|
function handleDisplayChange(value: string) {
|
|
const display = decodeScalar(location.query.display, DisplayModes.DURATION);
|
|
trackAnalytics('performance_views.transaction_summary.change_chart_display', {
|
|
organization,
|
|
from_chart: display,
|
|
to_chart: value,
|
|
});
|
|
|
|
browserHistory.push({
|
|
pathname: location.pathname,
|
|
query: {
|
|
...removeHistogramQueryStrings(location, [ZOOM_START, ZOOM_END]),
|
|
display: value,
|
|
},
|
|
});
|
|
}
|
|
|
|
function handleTrendDisplayChange(value: string) {
|
|
browserHistory.push({
|
|
pathname: location.pathname,
|
|
query: {...location.query, trendFunction: value},
|
|
});
|
|
}
|
|
|
|
function handleTrendColumnChange(value: string) {
|
|
browserHistory.push({
|
|
pathname: location.pathname,
|
|
query: {
|
|
...location.query,
|
|
trendParameter: value,
|
|
},
|
|
});
|
|
}
|
|
|
|
const TREND_PARAMETERS_OPTIONS: SelectValue<string>[] = TRENDS_PARAMETERS.map(
|
|
({label}) => ({
|
|
value: label,
|
|
label,
|
|
})
|
|
);
|
|
|
|
let display = decodeScalar(location.query.display, DisplayModes.DURATION);
|
|
let trendFunction = decodeScalar(
|
|
location.query.trendFunction,
|
|
TREND_FUNCTIONS_OPTIONS[0].value
|
|
) as TrendFunctionField;
|
|
let trendParameter = decodeScalar(
|
|
location.query.trendParameter,
|
|
TREND_PARAMETERS_OPTIONS[0].value
|
|
);
|
|
|
|
if (!Object.values(DisplayModes).includes(display as DisplayModes)) {
|
|
display = DisplayModes.DURATION;
|
|
}
|
|
if (!Object.values(TrendFunctionField).includes(trendFunction)) {
|
|
trendFunction = TrendFunctionField.P50;
|
|
}
|
|
if (
|
|
!Object.values(TrendParameterLabel).includes(trendParameter as TrendParameterLabel)
|
|
) {
|
|
trendParameter = TrendParameterLabel.DURATION;
|
|
}
|
|
|
|
const trendColumn =
|
|
TRENDS_PARAMETERS.find(parameter => parameter.label === trendParameter)?.column ||
|
|
TrendParameterColumn.DURATION;
|
|
|
|
const releaseQueryExtra = {
|
|
yAxis: display === DisplayModes.VITALS ? 'countVital' : 'countDuration',
|
|
showTransactions:
|
|
display === DisplayModes.VITALS
|
|
? TransactionsListOption.SLOW_LCP
|
|
: display === DisplayModes.DURATION
|
|
? TransactionsListOption.SLOW
|
|
: undefined,
|
|
};
|
|
|
|
const mepSetting = useMEPSettingContext();
|
|
const mepCardinalityContext = useMetricsCardinalityContext();
|
|
const queryExtras = getTransactionMEPParamsIfApplicable(
|
|
mepSetting,
|
|
mepCardinalityContext,
|
|
organization
|
|
);
|
|
|
|
return (
|
|
<Panel>
|
|
<ChartContainer data-test-id="transaction-summary-charts">
|
|
{display === DisplayModes.LATENCY && (
|
|
<LatencyChart
|
|
organization={organization}
|
|
location={location}
|
|
query={eventView.query}
|
|
project={eventView.project}
|
|
environment={eventView.environment}
|
|
start={eventView.start}
|
|
end={eventView.end}
|
|
statsPeriod={eventView.statsPeriod}
|
|
currentFilter={currentFilter}
|
|
totalCount={totalValue}
|
|
/>
|
|
)}
|
|
{display === DisplayModes.DURATION && (
|
|
<DurationChart
|
|
organization={organization}
|
|
query={eventView.query}
|
|
queryExtra={releaseQueryExtra}
|
|
project={eventView.project}
|
|
environment={eventView.environment}
|
|
start={eventView.start}
|
|
end={eventView.end}
|
|
statsPeriod={eventView.statsPeriod}
|
|
currentFilter={currentFilter}
|
|
withoutZerofill={withoutZerofill}
|
|
queryExtras={queryExtras}
|
|
/>
|
|
)}
|
|
{display === DisplayModes.DURATION_PERCENTILE && (
|
|
<DurationPercentileChart
|
|
organization={organization}
|
|
location={location}
|
|
query={eventView.query}
|
|
project={eventView.project}
|
|
environment={eventView.environment}
|
|
start={eventView.start}
|
|
end={eventView.end}
|
|
statsPeriod={eventView.statsPeriod}
|
|
currentFilter={currentFilter}
|
|
queryExtras={queryExtras}
|
|
/>
|
|
)}
|
|
{display === DisplayModes.TREND && (
|
|
<TrendChart
|
|
eventView={eventView}
|
|
trendFunction={trendFunction}
|
|
trendParameter={trendColumn}
|
|
organization={organization}
|
|
query={eventView.query}
|
|
queryExtra={releaseQueryExtra}
|
|
project={eventView.project}
|
|
environment={eventView.environment}
|
|
start={eventView.start}
|
|
end={eventView.end}
|
|
statsPeriod={eventView.statsPeriod}
|
|
withoutZerofill={withoutZerofill}
|
|
projects={project ? [project] : []}
|
|
withBreakpoint={organization.features.includes('performance-new-trends')}
|
|
/>
|
|
)}
|
|
{display === DisplayModes.VITALS && (
|
|
<VitalsChart
|
|
organization={organization}
|
|
query={eventView.query}
|
|
queryExtra={releaseQueryExtra}
|
|
project={eventView.project}
|
|
environment={eventView.environment}
|
|
start={eventView.start}
|
|
end={eventView.end}
|
|
statsPeriod={eventView.statsPeriod}
|
|
withoutZerofill={withoutZerofill}
|
|
queryExtras={queryExtras}
|
|
/>
|
|
)}
|
|
{display === DisplayModes.USER_MISERY && (
|
|
<UserMiseryChart
|
|
organization={organization}
|
|
query={eventView.query}
|
|
queryExtra={releaseQueryExtra}
|
|
project={eventView.project}
|
|
environment={eventView.environment}
|
|
start={eventView.start}
|
|
end={eventView.end}
|
|
statsPeriod={eventView.statsPeriod}
|
|
withoutZerofill={withoutZerofill}
|
|
/>
|
|
)}
|
|
</ChartContainer>
|
|
|
|
<ReversedChartControls>
|
|
<InlineContainer>
|
|
{display === DisplayModes.TREND && (
|
|
<OptionSelector
|
|
title={t('Percentile')}
|
|
selected={trendFunction}
|
|
options={TREND_FUNCTIONS_OPTIONS}
|
|
onChange={handleTrendDisplayChange}
|
|
/>
|
|
)}
|
|
{display === DisplayModes.TREND && (
|
|
<OptionSelector
|
|
title={t('Parameter')}
|
|
selected={trendParameter}
|
|
options={TREND_PARAMETERS_OPTIONS}
|
|
onChange={handleTrendColumnChange}
|
|
/>
|
|
)}
|
|
{display === DisplayModes.LATENCY && (
|
|
<LatencyChartControls location={location} />
|
|
)}
|
|
<OptionSelector
|
|
title={t('Display')}
|
|
selected={display}
|
|
options={generateDisplayOptions(currentFilter)}
|
|
onChange={handleDisplayChange}
|
|
/>
|
|
</InlineContainer>
|
|
</ReversedChartControls>
|
|
</Panel>
|
|
);
|
|
}
|
|
|
|
const ReversedChartControls = styled(ChartControls)`
|
|
flex-direction: row-reverse;
|
|
`;
|
|
|
|
export default TransactionSummaryCharts;
|