import {Fragment} from 'react';
import styled from '@emotion/styled';
import type {Location} from 'history';
import moment from 'moment-timezone';
import {LinkButton} from 'sentry/components/button';
import {getArbitraryRelativePeriod} from 'sentry/components/timeRangeSelector/utils';
import {DEFAULT_RELATIVE_PERIODS} from 'sentry/constants';
import {IconFire, IconOpen} from 'sentry/icons';
import {t} from 'sentry/locale';
import {space} from 'sentry/styles/space';
import type {Organization} from 'sentry/types/organization';
import type {Project} from 'sentry/types/project';
import {trackAnalytics} from 'sentry/utils/analytics';
import theme from 'sentry/utils/theme';
import normalizeUrl from 'sentry/utils/url/normalizeUrl';
import DetailPanel from 'sentry/views/insights/common/components/detailPanel';
import {
DisplayModes,
transactionSummaryRouteWithQuery,
} from 'sentry/views/performance/transactionSummary/utils';
import {FunctionsList} from 'sentry/views/performance/trends/changeExplorerUtils/functionsList';
import {MetricsTable} from 'sentry/views/performance/trends/changeExplorerUtils/metricsTable';
import {SpansList} from 'sentry/views/performance/trends/changeExplorerUtils/spansList';
import {Chart} from 'sentry/views/performance/trends/chart';
import type {
NormalizedTrendsTransaction,
TrendParameter,
TrendsStats,
TrendView,
} from 'sentry/views/performance/trends/types';
import {TrendChangeType} from 'sentry/views/performance/trends/types';
import {getTrendProjectId} from 'sentry/views/performance/trends/utils';
type PerformanceChangeExplorerProps = {
collapsed: boolean;
isLoading: boolean;
location: Location;
onClose: () => void;
organization: Organization;
projects: Project[];
statsData: TrendsStats;
transaction: NormalizedTrendsTransaction;
trendChangeType: TrendChangeType;
trendFunction: string;
trendParameter: TrendParameter;
trendView: TrendView;
};
type ExplorerBodyProps = {
isLoading: boolean;
location: Location;
organization: Organization;
projects: Project[];
statsData: TrendsStats;
transaction: NormalizedTrendsTransaction;
trendChangeType: TrendChangeType;
trendFunction: string;
trendParameter: TrendParameter;
trendView: TrendView;
};
type HeaderProps = {
organization: Organization;
projects: Project[];
transaction: NormalizedTrendsTransaction;
trendChangeType: TrendChangeType;
trendFunction: string;
trendParameter: TrendParameter;
trendView: TrendView;
};
export function PerformanceChangeExplorer({
collapsed,
transaction,
onClose,
trendChangeType,
trendFunction,
trendView,
statsData,
isLoading,
organization,
projects,
trendParameter,
location,
}: PerformanceChangeExplorerProps) {
return (
{!collapsed && (
)}
);
}
function ExplorerBody(props: ExplorerBodyProps) {
const {
transaction,
trendChangeType,
trendFunction,
trendView,
trendParameter,
isLoading,
location,
organization,
projects,
} = props;
const breakpointDate = transaction.breakpoint
? moment(transaction.breakpoint * 1000).format('ddd, DD MMM YYYY HH:mm:ss z')
: '';
const start = moment(trendView.start).format('DD MMM YYYY HH:mm:ss z');
const end = moment(trendView.end).format('DD MMM YYYY HH:mm:ss z');
return (
{`${trendParameter.label} (${trendFunction})`}
{trendView.statsPeriod
? DEFAULT_RELATIVE_PERIODS[trendView.statsPeriod] ||
getArbitraryRelativePeriod(trendView.statsPeriod)[trendView.statsPeriod]
: `${start} - ${end}`}
);
}
function InfoItem({label, value}: {label: string; value: string}) {
return (
{label}
{value}
);
}
function Header(props: HeaderProps) {
const {
transaction,
trendChangeType,
trendView,
projects,
organization,
trendFunction,
trendParameter,
} = props;
const regression = trendChangeType === TrendChangeType.REGRESSION;
const transactionSummaryLink = getTransactionSummaryLink(
trendView,
transaction,
projects,
organization,
trendFunction,
trendParameter
);
const handleClickAnalytics = () => {
trackAnalytics('performance_views.performance_change_explorer.summary_link_clicked', {
organization,
transaction: transaction.transaction,
});
};
return (
{regression ? t('Ongoing Regression') : t('Ongoing Improvement')}
{transaction.transaction}
}
aria-label={t('View transaction summary')}
onClick={handleClickAnalytics}
/>
);
}
function getTransactionSummaryLink(
eventView: TrendView,
transaction: NormalizedTrendsTransaction,
projects: Project[],
organization: Organization,
currentTrendFunction: string,
trendParameter: TrendParameter
) {
const summaryView = eventView.clone();
const projectID = getTrendProjectId(transaction, projects);
const target = transactionSummaryRouteWithQuery({
orgSlug: organization.slug,
transaction: String(transaction.transaction),
query: summaryView.generateQueryStringObject(),
projectID,
display: DisplayModes.TREND,
trendFunction: currentTrendFunction,
additionalQuery: {
trendParameter: trendParameter.column,
},
});
return target;
}
const PanelBodyWrapper = styled('div')`
padding: 0 ${space(2)};
margin-top: ${space(1)};
`;
const HeaderWrapper = styled('div')`
display: flex;
flex-wrap: nowrap;
margin-bottom: ${space(3)};
`;
const HeaderTextWrapper = styled('div')`
${p => p.theme.overflowEllipsis};
`;
type ChangeTypeProps = {regression: boolean};
const ChangeType = styled('p')`
color: ${p => (p.regression ? p.theme.danger : p.theme.success)};
margin-bottom: 0;
`;
const FireIcon = styled('div')`
padding: ${space(1.5)};
background-color: ${p => (p.regression ? p.theme.danger : p.theme.success)};
border-radius: ${space(0.5)};
margin-right: ${space(2)};
float: left;
height: 40px;
`;
const TransactionName = styled('h4')`
margin-right: ${space(1)};
margin-bottom: 0;
${p => p.theme.overflowEllipsis};
`;
const TransactionNameWrapper = styled('div')`
display: flex;
align-items: center;
margin-bottom: ${space(3)};
max-width: fit-content;
`;
const ViewTransactionButton = styled(LinkButton)`
padding: 0;
height: min-content;
min-height: 0px;
`;
const InfoLabel = styled('strong')`
color: ${p => p.theme.gray300};
`;
const InfoText = styled('h3')`
font-weight: ${p => p.theme.fontWeightNormal};
`;
const GraphPanel = styled('div')`
border: 1px solid ${p => p.theme.border};
border-radius: ${p => p.theme.panelBorderRadius};
margin-bottom: ${space(2)};
padding: ${space(3)};
display: block;
`;
export const ExplorerText = styled('p')<{
align?: string;
color?: string;
margin?: string;
}>`
margin-bottom: ${p => (p.margin ? p.margin : 0)};
color: ${p => p.color};
text-align: ${p => p.align};
`;