footer.tsx 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. import {Component} from 'react';
  2. import {browserHistory} from 'react-router';
  3. import * as Sentry from '@sentry/react';
  4. import {Location} from 'history';
  5. import {fetchTotalCount} from 'sentry/actionCreators/events';
  6. import {Client} from 'sentry/api';
  7. import OptionSelector from 'sentry/components/charts/optionSelector';
  8. import {
  9. ChartControls,
  10. InlineContainer,
  11. SectionHeading,
  12. SectionValue,
  13. } from 'sentry/components/charts/styles';
  14. import {t} from 'sentry/locale';
  15. import {Organization} from 'sentry/types';
  16. import {trackAnalytics} from 'sentry/utils/analytics';
  17. import EventView, {isAPIPayloadSimilar} from 'sentry/utils/discover/eventView';
  18. import {getAxisOptions, TooltipOption} from '../data';
  19. type Props = {
  20. api: Client;
  21. eventView: EventView;
  22. leftAxis: string;
  23. location: Location;
  24. organization: Organization;
  25. rightAxis: string;
  26. options?: TooltipOption[];
  27. };
  28. type State = {
  29. totalValues: null | number;
  30. };
  31. class ChartFooter extends Component<Props, State> {
  32. state: State = {
  33. totalValues: null,
  34. };
  35. componentDidMount() {
  36. this.mounted = true;
  37. this.fetchTotalCount();
  38. }
  39. componentDidUpdate(prevProps: Props) {
  40. const orgSlugHasChanged =
  41. this.props.organization.slug !== prevProps.organization.slug;
  42. const shouldRefetch = this.shouldRefetchData(prevProps);
  43. if ((orgSlugHasChanged || shouldRefetch) && this.props.eventView.isValid()) {
  44. this.fetchTotalCount();
  45. }
  46. }
  47. componentWillUnmount() {
  48. this.mounted = false;
  49. }
  50. shouldRefetchData = (prevProps: Props): boolean => {
  51. const thisAPIPayload = this.props.eventView.getEventsAPIPayload(this.props.location);
  52. const otherAPIPayload = prevProps.eventView.getEventsAPIPayload(prevProps.location);
  53. return !isAPIPayloadSimilar(thisAPIPayload, otherAPIPayload);
  54. };
  55. handleSelectorChange(key: string, value: string) {
  56. const {location, organization} = this.props;
  57. trackAnalytics('performance_views.overview.change_chart', {
  58. organization,
  59. metric: value,
  60. });
  61. browserHistory.push({
  62. pathname: location.pathname,
  63. query: {...location.query, [key]: value},
  64. });
  65. }
  66. mounted: boolean = false;
  67. async fetchTotalCount() {
  68. const {api, organization, location, eventView} = this.props;
  69. if (!eventView.isValid() || !this.mounted) {
  70. return;
  71. }
  72. try {
  73. const totals = await fetchTotalCount(
  74. api,
  75. organization.slug,
  76. eventView.getEventsAPIPayload(location)
  77. );
  78. if (this.mounted) {
  79. this.setState({totalValues: totals});
  80. }
  81. } catch (err) {
  82. Sentry.captureException(err);
  83. }
  84. }
  85. render() {
  86. const {leftAxis, organization, rightAxis} = this.props;
  87. const {totalValues} = this.state;
  88. const value = typeof totalValues === 'number' ? totalValues.toLocaleString() : '-';
  89. const options = this.props.options || getAxisOptions(organization);
  90. const leftOptions = options.map(opt => ({
  91. ...opt,
  92. disabled: opt.value === rightAxis,
  93. }));
  94. const rightOptions = options.map(opt => ({
  95. ...opt,
  96. disabled: opt.value === leftAxis,
  97. }));
  98. return (
  99. <ChartControls>
  100. <InlineContainer>
  101. <SectionHeading>{t('Total Events')}</SectionHeading>
  102. <SectionValue>{value}</SectionValue>
  103. </InlineContainer>
  104. <InlineContainer>
  105. <OptionSelector
  106. title={t('Display 1')}
  107. selected={leftAxis}
  108. options={leftOptions}
  109. onChange={(val: string) => this.handleSelectorChange('left', val)}
  110. />
  111. <OptionSelector
  112. title={t('Display 2')}
  113. selected={rightAxis}
  114. options={rightOptions}
  115. onChange={(val: string) => this.handleSelectorChange('right', val)}
  116. />
  117. </InlineContainer>
  118. </ChartControls>
  119. );
  120. }
  121. }
  122. export default ChartFooter;