projectErrorsBasicChart.tsx 3.5 KB

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