footer.tsx 3.9 KB

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