statusBreakdown.tsx 3.6 KB

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