useMetricsIntervalParam.tsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. import {useCallback, useEffect, useMemo} from 'react';
  2. import {
  3. getDiffInMinutes,
  4. GranularityLadder,
  5. ONE_HOUR,
  6. ONE_WEEK,
  7. SIXTY_DAYS,
  8. THIRTY_DAYS,
  9. TWENTY_FOUR_HOURS,
  10. TWO_WEEKS,
  11. } from 'sentry/components/charts/utils';
  12. import {parseStatsPeriod} from 'sentry/components/organizations/pageFilters/parse';
  13. import {t} from 'sentry/locale';
  14. import type {PageFilters} from 'sentry/types/core';
  15. import {parsePeriodToHours} from 'sentry/utils/dates';
  16. import {useUpdateQuery} from 'sentry/utils/metrics';
  17. import {decodeScalar} from 'sentry/utils/queryString';
  18. import useLocationQuery from 'sentry/utils/url/useLocationQuery';
  19. import usePageFilters from 'sentry/utils/usePageFilters';
  20. const ALL_INTERVAL_OPTIONS = [
  21. {value: '1m', label: t('1 minute')},
  22. {value: '5m', label: t('5 minutes')},
  23. {value: '15m', label: t('15 minutes')},
  24. {value: '30m', label: t('30 minutes')},
  25. {value: '1h', label: t('1 hour')},
  26. {value: '4h', label: t('4 hours')},
  27. {value: '1d', label: t('1 day')},
  28. {value: '1w', label: t('1 week')},
  29. {value: '4w', label: t('1 month')},
  30. ];
  31. const minimumInterval = new GranularityLadder([
  32. [SIXTY_DAYS, '1d'],
  33. [THIRTY_DAYS, '2h'],
  34. [TWO_WEEKS, '1h'],
  35. [ONE_WEEK, '30m'],
  36. [TWENTY_FOUR_HOURS, '5m'],
  37. [ONE_HOUR, '1m'],
  38. [0, '1m'],
  39. ]);
  40. const maximumInterval = new GranularityLadder([
  41. [SIXTY_DAYS, '4w'],
  42. [THIRTY_DAYS, '1w'],
  43. [TWO_WEEKS, '1w'],
  44. [ONE_WEEK, '1d'],
  45. [TWENTY_FOUR_HOURS, '6h'],
  46. [ONE_HOUR, '15m'],
  47. [0, '1m'],
  48. ]);
  49. export function getIntervalOptionsForStatsPeriod(datetime: PageFilters['datetime']) {
  50. const diffInMinutes = getDiffInMinutes(datetime);
  51. const minimumOption = minimumInterval.getInterval(diffInMinutes);
  52. const minimumOptionInHours = parsePeriodToHours(minimumOption);
  53. const maximumOption = maximumInterval.getInterval(diffInMinutes);
  54. const maximumOptionInHours = parsePeriodToHours(maximumOption);
  55. return ALL_INTERVAL_OPTIONS.filter(option => {
  56. const optionInHours = parsePeriodToHours(option.value);
  57. return optionInHours >= minimumOptionInHours && optionInHours <= maximumOptionInHours;
  58. });
  59. }
  60. export function validateInterval(
  61. interval: string,
  62. options: {label: string; value: string}[]
  63. ) {
  64. const isPeriod = !!parseStatsPeriod(interval);
  65. const currentIntervalValues = options.map(option => option.value);
  66. return isPeriod && currentIntervalValues.includes(interval)
  67. ? interval
  68. : // Take the 2nd most granular option if available
  69. options[1]?.value ?? options[0].value;
  70. }
  71. export function useMetricsIntervalParam() {
  72. const {datetime} = usePageFilters().selection;
  73. const {interval} = useLocationQuery({fields: {interval: decodeScalar}});
  74. const updateQuery = useUpdateQuery();
  75. const handleIntervalChange = useCallback(
  76. (newInterval: string) => {
  77. updateQuery({interval: newInterval}, {replace: true});
  78. },
  79. [updateQuery]
  80. );
  81. const metricsIntervalOptions = useMetricsIntervalOptions({
  82. interval,
  83. datetime,
  84. onIntervalChange: handleIntervalChange,
  85. });
  86. useEffect(() => {
  87. if (interval !== metricsIntervalOptions.interval) {
  88. handleIntervalChange(metricsIntervalOptions.interval);
  89. }
  90. }, [interval, metricsIntervalOptions.interval, handleIntervalChange]);
  91. return metricsIntervalOptions;
  92. }
  93. export interface MetricsIntervalParamProps {
  94. datetime: PageFilters['datetime'];
  95. interval: string;
  96. onIntervalChange: (interval: string) => void;
  97. }
  98. export function useMetricsIntervalOptions({
  99. interval,
  100. datetime,
  101. onIntervalChange,
  102. }: MetricsIntervalParamProps) {
  103. const currentIntervalOptions = useMemo(
  104. () => getIntervalOptionsForStatsPeriod(datetime),
  105. [datetime]
  106. );
  107. const setInterval = useCallback(
  108. (newInterval: string) => {
  109. onIntervalChange(newInterval);
  110. },
  111. [onIntervalChange]
  112. );
  113. const validatedInterval = useMemo(
  114. () => validateInterval(interval, currentIntervalOptions),
  115. [interval, currentIntervalOptions]
  116. );
  117. return {
  118. interval: validatedInterval,
  119. setInterval,
  120. currentIntervalOptions,
  121. };
  122. }