projectErrorsBasicChart.tsx 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. import {browserHistory} from 'react-router';
  2. import type {BarSeriesOption} from 'echarts';
  3. import type {Location} from 'history';
  4. import BaseChart from 'sentry/components/charts/baseChart';
  5. import {HeaderTitleLegend} from 'sentry/components/charts/styles';
  6. import TransitionChart from 'sentry/components/charts/transitionChart';
  7. import TransparentLoadingMask from 'sentry/components/charts/transparentLoadingMask';
  8. import DeprecatedAsyncComponent from 'sentry/components/deprecatedAsyncComponent';
  9. import {DEFAULT_STATS_PERIOD} from 'sentry/constants';
  10. import {t} from 'sentry/locale';
  11. import type {Organization} from 'sentry/types/organization';
  12. import type {Project} from 'sentry/types/project';
  13. import getDynamicText from 'sentry/utils/getDynamicText';
  14. export const ERRORS_BASIC_CHART_PERIODS = ['1h', '24h', '7d', '14d', '30d'];
  15. type Props = DeprecatedAsyncComponent['props'] & {
  16. location: Location;
  17. onTotalValuesChange: (value: number | null) => void;
  18. organization: Organization;
  19. projectId?: string;
  20. };
  21. type State = DeprecatedAsyncComponent['state'] & {
  22. projects: Project[] | null;
  23. };
  24. class ProjectErrorsBasicChart extends DeprecatedAsyncComponent<Props, State> {
  25. getDefaultState() {
  26. return {
  27. ...super.getDefaultState(),
  28. projects: null,
  29. };
  30. }
  31. getEndpoints(): ReturnType<DeprecatedAsyncComponent['getEndpoints']> {
  32. const {organization, projectId} = this.props;
  33. if (!projectId) {
  34. return [];
  35. }
  36. return [
  37. [
  38. 'projects',
  39. `/organizations/${organization.slug}/projects/`,
  40. {
  41. query: {
  42. statsPeriod: this.getStatsPeriod(),
  43. query: `id:${projectId}`,
  44. },
  45. },
  46. ],
  47. ];
  48. }
  49. componentDidMount() {
  50. super.componentDidMount();
  51. const {location} = this.props;
  52. if (!ERRORS_BASIC_CHART_PERIODS.includes(location.query.statsPeriod)) {
  53. browserHistory.replace({
  54. pathname: location.pathname,
  55. query: {
  56. ...location.query,
  57. statsPeriod: this.getStatsPeriod(),
  58. start: undefined,
  59. end: undefined,
  60. },
  61. });
  62. }
  63. }
  64. onLoadAllEndpointsSuccess() {
  65. this.props.onTotalValuesChange(
  66. this.state.projects?.[0]?.stats?.reduce((acc, [, value]) => acc + value, 0) ?? null
  67. );
  68. }
  69. getStatsPeriod() {
  70. const {location} = this.props;
  71. const statsPeriod = location.query.statsPeriod;
  72. if (ERRORS_BASIC_CHART_PERIODS.includes(statsPeriod)) {
  73. return statsPeriod;
  74. }
  75. return DEFAULT_STATS_PERIOD;
  76. }
  77. getSeries(): BarSeriesOption[] {
  78. const {projects} = this.state;
  79. return [
  80. {
  81. cursor: 'normal' as const,
  82. name: t('Errors'),
  83. type: 'bar',
  84. data:
  85. projects?.[0]?.stats?.map(([timestamp, value]) => [timestamp * 1000, value]) ??
  86. [],
  87. },
  88. ];
  89. }
  90. renderLoading() {
  91. return this.renderBody();
  92. }
  93. renderBody() {
  94. const {loading, reloading} = this.state;
  95. return getDynamicText({
  96. value: (
  97. <TransitionChart loading={loading} reloading={reloading}>
  98. <TransparentLoadingMask visible={reloading} />
  99. <HeaderTitleLegend>{t('Daily Errors')}</HeaderTitleLegend>
  100. <BaseChart
  101. series={this.getSeries()}
  102. isGroupedByDate
  103. showTimeInTooltip
  104. colors={theme => [theme.purple300, theme.purple200]}
  105. grid={{left: '10px', right: '10px', top: '40px', bottom: '0px'}}
  106. />
  107. </TransitionChart>
  108. ),
  109. fixed: t('Number of Errors Chart'),
  110. });
  111. }
  112. }
  113. export default ProjectErrorsBasicChart;