projectsPreviewTable.tsx 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. import {Fragment, useMemo} from 'react';
  2. import {css} from '@emotion/react';
  3. import styled from '@emotion/styled';
  4. import LoadingIndicator from 'sentry/components/loadingIndicator';
  5. import Panel from 'sentry/components/panels/panel';
  6. import {t} from 'sentry/locale';
  7. import {space} from 'sentry/styles/space';
  8. import {useDebouncedValue} from 'sentry/utils/useDebouncedValue';
  9. import {ProjectsTable} from 'sentry/views/settings/dynamicSampling/projectsTable';
  10. import {SamplingBreakdown} from 'sentry/views/settings/dynamicSampling/samplingBreakdown';
  11. import {formatPercent} from 'sentry/views/settings/dynamicSampling/utils/formatPercent';
  12. import {organizationSamplingForm} from 'sentry/views/settings/dynamicSampling/utils/organizationSamplingForm';
  13. import {parsePercent} from 'sentry/views/settings/dynamicSampling/utils/parsePercent';
  14. import {balanceSampleRate} from 'sentry/views/settings/dynamicSampling/utils/rebalancing';
  15. import type {ProjectSampleCount} from 'sentry/views/settings/dynamicSampling/utils/useProjectSampleCounts';
  16. const {useFormField} = organizationSamplingForm;
  17. interface Props {
  18. isLoading: boolean;
  19. sampleCounts: ProjectSampleCount[];
  20. }
  21. export function ProjectsPreviewTable({isLoading, sampleCounts}: Props) {
  22. const {value: targetSampleRate, initialValue: initialTargetSampleRate} =
  23. useFormField('targetSampleRate');
  24. const debouncedTargetSampleRate = useDebouncedValue(
  25. targetSampleRate,
  26. // For longer lists we debounce the input to avoid too many re-renders
  27. sampleCounts.length > 100 ? 200 : 0
  28. );
  29. const balancingItems = useMemo(
  30. () =>
  31. sampleCounts.map(item => ({
  32. ...item,
  33. // Add properties to match the BalancingItem type of the balanceSampleRate function
  34. id: item.project.id,
  35. sampleRate: 1,
  36. })),
  37. [sampleCounts]
  38. );
  39. const {balancedItems} = useMemo(() => {
  40. const targetRate = parsePercent(debouncedTargetSampleRate);
  41. return balanceSampleRate({
  42. targetSampleRate: targetRate,
  43. items: balancingItems,
  44. });
  45. }, [debouncedTargetSampleRate, balancingItems]);
  46. const initialSampleRateById = useMemo(() => {
  47. const targetRate = parsePercent(initialTargetSampleRate);
  48. const {balancedItems: initialBalancedItems} = balanceSampleRate({
  49. targetSampleRate: targetRate,
  50. items: balancingItems,
  51. });
  52. return initialBalancedItems.reduce((acc, item) => {
  53. acc[item.id] = item.sampleRate;
  54. return acc;
  55. }, {});
  56. }, [initialTargetSampleRate, balancingItems]);
  57. const itemsWithFormattedNumbers = useMemo(() => {
  58. return balancedItems.map(item => ({
  59. ...item,
  60. sampleRate: formatPercent(item.sampleRate),
  61. initialSampleRate: formatPercent(initialSampleRateById[item.id]),
  62. }));
  63. }, [balancedItems, initialSampleRateById]);
  64. const breakdownSampleRates = balancedItems.reduce((acc, item) => {
  65. acc[item.id] = item.sampleRate;
  66. return acc;
  67. }, {});
  68. return (
  69. <Fragment>
  70. <BreakdownPanel>
  71. {isLoading ? (
  72. <LoadingIndicator
  73. css={css`
  74. margin: ${space(4)} 0;
  75. `}
  76. />
  77. ) : (
  78. <SamplingBreakdown
  79. sampleCounts={sampleCounts}
  80. sampleRates={breakdownSampleRates}
  81. />
  82. )}
  83. </BreakdownPanel>
  84. <ProjectsTable
  85. stickyHeaders
  86. rateHeader={t('Estimated Rate')}
  87. inputTooltip={t('To edit project sample rates, switch to manual sampling mode.')}
  88. emptyMessage={t('No active projects found in the selected period.')}
  89. isEmpty={!sampleCounts.length}
  90. isLoading={isLoading}
  91. items={itemsWithFormattedNumbers}
  92. />
  93. </Fragment>
  94. );
  95. }
  96. const BreakdownPanel = styled(Panel)`
  97. margin-bottom: ${space(3)};
  98. padding: ${space(2)};
  99. `;