statusBreakdown.tsx 3.5 KB

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