projectErrorsBasicChart.tsx 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. import {Fragment, useEffect, useMemo} from 'react';
  2. import {useSearchParams} from 'react-router-dom';
  3. import type {BarSeriesOption} from 'echarts';
  4. import BaseChart from 'sentry/components/charts/baseChart';
  5. import LoadingPanel from 'sentry/components/charts/loadingPanel';
  6. import {HeaderTitleLegend} from 'sentry/components/charts/styles';
  7. import LoadingError from 'sentry/components/loadingError';
  8. import {DEFAULT_STATS_PERIOD} from 'sentry/constants';
  9. import {t} from 'sentry/locale';
  10. import type {Project} from 'sentry/types/project';
  11. import {defined} from 'sentry/utils';
  12. import {useApiQuery} from 'sentry/utils/queryClient';
  13. import useOrganization from 'sentry/utils/useOrganization';
  14. export const ERRORS_BASIC_CHART_PERIODS = ['1h', '24h', '7d', '14d', '30d'];
  15. type Props = {
  16. onTotalValuesChange: (value: number | null) => void;
  17. projectId?: string;
  18. };
  19. function ProjectErrorsBasicChart({projectId, onTotalValuesChange}: Props) {
  20. const organization = useOrganization();
  21. const [searchParams, setSearchParams] = useSearchParams();
  22. const urlStatsPeriod = searchParams.get('statsPeriod') ?? '';
  23. const statsPeriod = ERRORS_BASIC_CHART_PERIODS.includes(urlStatsPeriod)
  24. ? urlStatsPeriod
  25. : DEFAULT_STATS_PERIOD;
  26. const {
  27. data: projects,
  28. isLoading,
  29. isError,
  30. isSuccess,
  31. } = useApiQuery<Project[]>(
  32. [
  33. `/organizations/${organization.slug}/projects/`,
  34. {
  35. query: {
  36. statsPeriod,
  37. query: `id:${projectId}`,
  38. },
  39. },
  40. ],
  41. {
  42. staleTime: 0,
  43. enabled: defined(projectId),
  44. }
  45. );
  46. const stats = useMemo(() => {
  47. return projects?.[0]?.stats ?? [];
  48. }, [projects]);
  49. const totalValues = stats.reduce((acc, [, value]) => acc + value, 0);
  50. useEffect(() => {
  51. if (!ERRORS_BASIC_CHART_PERIODS.includes(urlStatsPeriod)) {
  52. setSearchParams(oldParams => {
  53. oldParams.set('statsPeriod', DEFAULT_STATS_PERIOD);
  54. oldParams.delete('start');
  55. oldParams.delete('end');
  56. return oldParams;
  57. });
  58. }
  59. }, [setSearchParams, urlStatsPeriod]);
  60. useEffect(() => {
  61. if (isSuccess) {
  62. onTotalValuesChange(totalValues);
  63. }
  64. }, [isSuccess, onTotalValuesChange, totalValues]);
  65. if (isLoading) {
  66. return <LoadingPanel height="200px" data-test-id="events-request-loading" />;
  67. }
  68. if (isError) {
  69. return <LoadingError />;
  70. }
  71. const series: BarSeriesOption[] = [
  72. {
  73. cursor: 'normal' as const,
  74. name: t('Errors'),
  75. type: 'bar',
  76. data: stats.map(([timestamp, value]) => [timestamp * 1000, value]) ?? [],
  77. },
  78. ];
  79. return (
  80. <Fragment>
  81. <HeaderTitleLegend>{t('Daily Errors')}</HeaderTitleLegend>
  82. <BaseChart
  83. series={series}
  84. isGroupedByDate
  85. showTimeInTooltip
  86. colors={theme => [theme.purple300, theme.purple200]}
  87. grid={{left: '10px', right: '10px', top: '40px', bottom: '0px'}}
  88. />
  89. </Fragment>
  90. );
  91. }
  92. export default ProjectErrorsBasicChart;