statusBreakdown.tsx 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. import {Fragment} from 'react';
  2. import {browserHistory} from 'react-router';
  3. import styled from '@emotion/styled';
  4. import {Location} from 'history';
  5. import BreakdownBars from 'sentry/components/charts/breakdownBars';
  6. import ErrorPanel from 'sentry/components/charts/errorPanel';
  7. import {SectionHeading} from 'sentry/components/charts/styles';
  8. import EmptyStateWarning from 'sentry/components/emptyStateWarning';
  9. import Placeholder from 'sentry/components/placeholder';
  10. import QuestionTooltip from 'sentry/components/questionTooltip';
  11. import {IconWarning} from 'sentry/icons';
  12. import {t} from 'sentry/locale';
  13. import {Organization} from 'sentry/types';
  14. import trackAdvancedAnalyticsEvent from 'sentry/utils/analytics/trackAdvancedAnalyticsEvent';
  15. import DiscoverQuery from 'sentry/utils/discover/discoverQuery';
  16. import EventView from 'sentry/utils/discover/eventView';
  17. import {MutableSearch} from 'sentry/utils/tokenizeSearch';
  18. import {getTermHelp, PERFORMANCE_TERM} from 'sentry/views/performance/data';
  19. type Props = {
  20. eventView: EventView;
  21. location: Location;
  22. organization: Organization;
  23. };
  24. function StatusBreakdown({eventView, location, organization}: Props) {
  25. const useEvents = organization.features.includes(
  26. 'performance-frontend-use-events-endpoint'
  27. );
  28. const breakdownView = eventView
  29. .withColumns([
  30. {kind: 'function', function: ['count', '', '', undefined]},
  31. {kind: 'field', field: 'transaction.status'},
  32. ])
  33. .withSorts([{kind: 'desc', field: 'count'}]);
  34. return (
  35. <Fragment>
  36. <SectionHeading>
  37. {t('Status Breakdown')}
  38. <QuestionTooltip
  39. position="top"
  40. title={getTermHelp(organization, PERFORMANCE_TERM.STATUS_BREAKDOWN)}
  41. size="sm"
  42. />
  43. </SectionHeading>
  44. <DiscoverQuery
  45. eventView={breakdownView}
  46. location={location}
  47. orgSlug={organization.slug}
  48. referrer="api.performance.status-breakdown"
  49. useEvents={useEvents}
  50. >
  51. {({isLoading, error, tableData}) => {
  52. if (isLoading) {
  53. return <Placeholder height="124px" />;
  54. }
  55. if (error) {
  56. return (
  57. <ErrorPanel height="124px">
  58. <IconWarning color="gray300" size="lg" />
  59. </ErrorPanel>
  60. );
  61. }
  62. if (!tableData || tableData.data.length === 0) {
  63. return (
  64. <EmptyStatusBreakdown small>{t('No statuses found')}</EmptyStatusBreakdown>
  65. );
  66. }
  67. const points = tableData.data.map(row => ({
  68. label: String(row['transaction.status']),
  69. value: parseInt(String(row[useEvents ? 'count()' : 'count']), 10),
  70. onClick: () => {
  71. const query = new MutableSearch(eventView.query);
  72. query
  73. .removeFilter('!transaction.status')
  74. .setFilterValues('transaction.status', [
  75. row['transaction.status'] as string,
  76. ]);
  77. browserHistory.push({
  78. pathname: location.pathname,
  79. query: {
  80. ...location.query,
  81. cursor: undefined,
  82. query: query.formatString(),
  83. },
  84. });
  85. trackAdvancedAnalyticsEvent(
  86. 'performance_views.transaction_summary.status_breakdown_click',
  87. {
  88. organization,
  89. status: row['transaction.status'] as string,
  90. }
  91. );
  92. },
  93. }));
  94. return <BreakdownBars data={points} />;
  95. }}
  96. </DiscoverQuery>
  97. </Fragment>
  98. );
  99. }
  100. const EmptyStatusBreakdown = styled(EmptyStateWarning)`
  101. height: 124px;
  102. padding: 50px 15%;
  103. `;
  104. export default StatusBreakdown;