teamResolutionTime.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. import styled from '@emotion/styled';
  2. import AsyncComponent from 'sentry/components/asyncComponent';
  3. import {BarChart} from 'sentry/components/charts/barChart';
  4. import {DateTimeObject} from 'sentry/components/charts/utils';
  5. import LoadingIndicator from 'sentry/components/loadingIndicator';
  6. import {normalizeDateTimeParams} from 'sentry/components/organizations/pageFilters/parse';
  7. import {t} from 'sentry/locale';
  8. import space from 'sentry/styles/space';
  9. import {Organization} from 'sentry/types';
  10. import {getDuration} from 'sentry/utils/formatters';
  11. import {barAxisLabel, sortSeriesByDay} from './utils';
  12. type TimeToResolution = Record<string, {avg: number; count: number}>;
  13. type Props = AsyncComponent['props'] & {
  14. organization: Organization;
  15. teamSlug: string;
  16. environment?: string;
  17. } & DateTimeObject;
  18. type State = AsyncComponent['state'] & {
  19. resolutionTime: TimeToResolution | null;
  20. };
  21. class TeamResolutionTime extends AsyncComponent<Props, State> {
  22. shouldRenderBadRequests = true;
  23. getDefaultState(): State {
  24. return {
  25. ...super.getDefaultState(),
  26. resolutionTime: null,
  27. };
  28. }
  29. getEndpoints(): ReturnType<AsyncComponent['getEndpoints']> {
  30. const {organization, start, end, period, utc, teamSlug, environment} = this.props;
  31. const datetime = {start, end, period, utc};
  32. return [
  33. [
  34. 'resolutionTime',
  35. `/teams/${organization.slug}/${teamSlug}/time-to-resolution/`,
  36. {
  37. query: {
  38. ...normalizeDateTimeParams(datetime),
  39. environment,
  40. },
  41. },
  42. ],
  43. ];
  44. }
  45. componentDidUpdate(prevProps: Props) {
  46. const {start, end, period, utc, teamSlug, environment} = this.props;
  47. if (
  48. prevProps.start !== start ||
  49. prevProps.end !== end ||
  50. prevProps.period !== period ||
  51. prevProps.utc !== utc ||
  52. prevProps.teamSlug !== teamSlug ||
  53. prevProps.environment !== environment
  54. ) {
  55. this.remountComponent();
  56. }
  57. }
  58. renderLoading() {
  59. return (
  60. <ChartWrapper>
  61. <LoadingIndicator />
  62. </ChartWrapper>
  63. );
  64. }
  65. renderBody() {
  66. const {resolutionTime} = this.state;
  67. const data = Object.entries(resolutionTime ?? {}).map(([bucket, {avg}]) => ({
  68. value: avg,
  69. name: new Date(bucket).getTime(),
  70. }));
  71. const seriesData = sortSeriesByDay(data);
  72. return (
  73. <ChartWrapper>
  74. <BarChart
  75. style={{height: 190}}
  76. isGroupedByDate
  77. useShortDate
  78. period="7d"
  79. tooltip={{
  80. valueFormatter: (value: number) => getDuration(value, 1),
  81. }}
  82. yAxis={{
  83. // Each yAxis marker will increase by 1 day
  84. minInterval: 86400,
  85. axisLabel: {
  86. formatter: (value: number) => {
  87. if (value === 0) {
  88. return '';
  89. }
  90. return getDuration(value, 0, true, true);
  91. },
  92. },
  93. }}
  94. legend={{right: 0, top: 0}}
  95. xAxis={barAxisLabel(seriesData.length)}
  96. series={[
  97. {
  98. seriesName: t('Time to Resolution'),
  99. data: seriesData,
  100. silent: true,
  101. barCategoryGap: '5%',
  102. },
  103. ]}
  104. />
  105. </ChartWrapper>
  106. );
  107. }
  108. }
  109. export default TeamResolutionTime;
  110. const ChartWrapper = styled('div')`
  111. padding: ${space(2)} ${space(2)} 0 ${space(2)};
  112. `;