footer.tsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  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 {trackAnalyticsEvent} 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. trackAnalyticsEvent({
  58. eventKey: 'performance_views.overview.change_chart',
  59. eventName: 'Performance Views: Change Overview Chart',
  60. organization_id: parseInt(organization.id, 10),
  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;