noDataMessage.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. import {Fragment} from 'react';
  2. import sumBy from 'lodash/sumBy';
  3. import ExternalLink from 'sentry/components/links/externalLink';
  4. import {t, tct} from 'sentry/locale';
  5. import useOrganization from 'sentry/utils/useOrganization';
  6. import usePageFilters from 'sentry/utils/usePageFilters';
  7. import {normalizeUrl} from 'sentry/utils/withDomainRequired';
  8. import {useIneligibleProjects} from 'sentry/views/performance/database/useIneligibleProjects';
  9. import {useProjectSpanMetricCounts} from 'sentry/views/starfish/queries/useProjectSpanMetricsCounts';
  10. interface Props {
  11. Wrapper?: React.ComponentType;
  12. }
  13. function DivWrapper(props) {
  14. return <div {...props} />;
  15. }
  16. export function NoDataMessage({Wrapper = DivWrapper}: Props) {
  17. const {selection, isReady: pageFilterIsReady} = usePageFilters();
  18. const selectedProjectIds = selection.projects.map(projectId => projectId.toString());
  19. const {data: projectSpanMetricsCounts, isLoading: areSpanMetricCountsLoading} =
  20. useProjectSpanMetricCounts({
  21. query: 'span.module:db',
  22. statsPeriod: SAMPLE_STATS_PERIOD,
  23. enabled: pageFilterIsReady,
  24. projectId: selectedProjectIds,
  25. });
  26. const doesAnySelectedProjectHaveMetrics =
  27. sumBy(projectSpanMetricsCounts, 'count()') > 0;
  28. const {ineligibleProjects, isFetching: areIneligibleProjectsFetching} =
  29. useIneligibleProjects({
  30. projectId: selectedProjectIds,
  31. enabled: pageFilterIsReady && !doesAnySelectedProjectHaveMetrics,
  32. });
  33. const organization = useOrganization();
  34. const hasMoreIneligibleProjectsThanVisible =
  35. ineligibleProjects.length > MAX_LISTED_PROJECTS;
  36. if (areSpanMetricCountsLoading || areIneligibleProjectsFetching) {
  37. return null;
  38. }
  39. if (!projectSpanMetricsCounts) {
  40. return null;
  41. }
  42. if (doesAnySelectedProjectHaveMetrics) {
  43. return null;
  44. }
  45. const firstIneligibleProjects = ineligibleProjects.slice(0, MAX_LISTED_PROJECTS + 1);
  46. return (
  47. <Wrapper>
  48. {t('No queries found.')}{' '}
  49. {tct(
  50. 'Try updating your filters, or learn more about performance monitoring for queries in our [documentation:documentation].',
  51. {
  52. documentation: (
  53. <ExternalLink href="https://docs.sentry.io/product/performance/queries/" />
  54. ),
  55. }
  56. )}{' '}
  57. {ineligibleProjects.length > 0 &&
  58. tct('You may also be missing data due to outdated SDKs: [projectList]', {
  59. documentation: (
  60. <ExternalLink href="https://docs.sentry.io/product/performance/query-insights/" />
  61. ),
  62. projectList: (
  63. <Fragment>
  64. {firstIneligibleProjects.map((project, projectIndex) => {
  65. return (
  66. <span key={project.id}>
  67. <a
  68. href={normalizeUrl(
  69. `/organizations/${organization.slug}/projects/${project.slug}/`
  70. )}
  71. >
  72. {project.name}
  73. </a>
  74. {projectIndex < firstIneligibleProjects.length - 1 && ', '}
  75. </span>
  76. );
  77. })}
  78. </Fragment>
  79. ),
  80. })}
  81. {hasMoreIneligibleProjectsThanVisible &&
  82. tct(' and [count] more.', {
  83. count: ineligibleProjects.length - MAX_LISTED_PROJECTS,
  84. })}{' '}
  85. </Wrapper>
  86. );
  87. }
  88. const MAX_LISTED_PROJECTS = 3;
  89. const SAMPLE_STATS_PERIOD = '14d'; // The time period in which to check for any presence of span metrics