123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210 |
- import {Component, Fragment} from 'react';
- import styled from '@emotion/styled';
- import {Location} from 'history';
- import {promptsCheck, promptsUpdate} from 'sentry/actionCreators/prompts';
- import {Client} from 'sentry/api';
- import Alert from 'sentry/components/alert';
- import Button from 'sentry/components/button';
- import ErrorBoundary from 'sentry/components/errorBoundary';
- import ExternalLink from 'sentry/components/links/externalLink';
- import Link from 'sentry/components/links/link';
- import Placeholder from 'sentry/components/placeholder';
- import QuickTrace from 'sentry/components/quickTrace';
- import {generateTraceTarget} from 'sentry/components/quickTrace/utils';
- import {IconClose} from 'sentry/icons';
- import {t, tct} from 'sentry/locale';
- import space from 'sentry/styles/space';
- import {Organization} from 'sentry/types';
- import {Event} from 'sentry/types/event';
- import {trackAnalyticsEvent} from 'sentry/utils/analytics';
- import trackAdvancedAnalyticsEvent from 'sentry/utils/analytics/trackAdvancedAnalyticsEvent';
- import {QuickTraceQueryChildrenProps} from 'sentry/utils/performance/quickTrace/types';
- import {promptIsDismissed} from 'sentry/utils/promptIsDismissed';
- import withApi from 'sentry/utils/withApi';
- type Props = {
- api: Client;
- event: Event;
- location: Location;
- organization: Organization;
- quickTrace: QuickTraceQueryChildrenProps;
- isPerformanceIssue?: boolean;
- };
- type State = {
- shouldShow: boolean | null;
- };
- class IssueQuickTrace extends Component<Props, State> {
- state: State = {
- shouldShow: null,
- };
- componentDidMount() {
- this.promptsCheck();
- }
- shouldComponentUpdate(nextProps, nextState: State) {
- return (
- this.props.event !== nextProps.event ||
- this.state.shouldShow !== nextState.shouldShow ||
- this.props.quickTrace !== nextProps.quickTrace
- );
- }
- async promptsCheck() {
- const {api, event, organization} = this.props;
- const data = await promptsCheck(api, {
- organizationId: organization.id,
- projectId: event.projectID,
- feature: 'quick_trace_missing',
- });
- this.setState({shouldShow: !promptIsDismissed(data ?? {}, 30)});
- }
- handleTraceLink(organization: Organization) {
- trackAnalyticsEvent({
- eventKey: 'quick_trace.trace_id.clicked',
- eventName: 'Quick Trace: Trace ID clicked',
- organization_id: parseInt(organization.id, 10),
- source: 'issues',
- });
- }
- renderTraceLink({isLoading, error, trace, type}) {
- const {event, organization} = this.props;
- if (isLoading || error !== null || trace === null || type === 'empty') {
- return null;
- }
- return (
- <LinkContainer>
- <Link
- to={generateTraceTarget(event, organization)}
- onClick={() => this.handleTraceLink(organization)}
- >
- {t('View Full Trace')}
- </Link>
- </LinkContainer>
- );
- }
- snoozePrompt = () => {
- const {api, event, organization} = this.props;
- const data = {
- projectId: event.projectID,
- organizationId: organization.id,
- feature: 'quick_trace_missing',
- status: 'snoozed' as const,
- };
- promptsUpdate(api, data).then(() => this.setState({shouldShow: false}));
- };
- renderQuickTrace(results: QuickTraceQueryChildrenProps) {
- const {event, location, organization, isPerformanceIssue} = this.props;
- const {shouldShow} = this.state;
- const {isLoading, error, trace, type} = results;
- if (isLoading) {
- return <Placeholder height="24px" />;
- }
- if (error || trace === null || trace.length === 0) {
- if (!shouldShow) {
- return null;
- }
- trackAdvancedAnalyticsEvent('issue.quick_trace_status', {
- organization,
- status: type === 'missing' ? 'transaction missing' : 'trace missing',
- is_performance_issue: isPerformanceIssue ?? false,
- });
- return (
- <StyledAlert
- type="info"
- showIcon
- trailingItems={
- <Button
- priority="link"
- size="zero"
- title={t('Dismiss for a month')}
- onClick={this.snoozePrompt}
- >
- <IconClose />
- </Button>
- }
- >
- {tct(
- 'The [type] for this event cannot be found. [link:Read the docs to understand why].',
- {
- type: type === 'missing' ? t('transaction') : t('trace'),
- link: (
- <ExternalLink href="https://docs.sentry.io/product/sentry-basics/tracing/trace-view/#troubleshooting" />
- ),
- }
- )}
- </StyledAlert>
- );
- }
- trackAdvancedAnalyticsEvent('issue.quick_trace_status', {
- organization,
- status: 'success',
- is_performance_issue: isPerformanceIssue ?? false,
- });
- return (
- <Fragment>
- {this.renderTraceLink(results)}
- <QuickTraceWrapper>
- <QuickTrace
- event={event}
- quickTrace={results}
- location={location}
- organization={organization}
- anchor="left"
- errorDest="issue"
- transactionDest="performance"
- />
- </QuickTraceWrapper>
- </Fragment>
- );
- }
- render() {
- const {quickTrace} = this.props;
- return <ErrorBoundary mini>{this.renderQuickTrace(quickTrace)}</ErrorBoundary>;
- }
- }
- const LinkContainer = styled('span')`
- margin-left: ${space(1)};
- padding-left: ${space(1)};
- position: relative;
- &:before {
- display: block;
- position: absolute;
- content: '';
- left: 0;
- top: 2px;
- height: 14px;
- border-left: 1px solid ${p => p.theme.border};
- }
- `;
- const QuickTraceWrapper = styled('div')`
- margin-top: ${space(0.5)};
- `;
- const StyledAlert = styled(Alert)`
- margin: 0;
- `;
- export default withApi(IssueQuickTrace);
|