noDataMessage.tsx 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. import {Fragment} from 'react';
  2. import {openHelpSearchModal} from 'sentry/actionCreators/modal';
  3. import {Button} from 'sentry/components/button';
  4. import ExternalLink from 'sentry/components/links/externalLink';
  5. import {t, tct} from 'sentry/locale';
  6. import type {Project} from 'sentry/types/project';
  7. import useOrganization from 'sentry/utils/useOrganization';
  8. import usePageFilters from 'sentry/utils/usePageFilters';
  9. import {normalizeUrl} from 'sentry/utils/withDomainRequired';
  10. import {useDenylistedProjects} from 'sentry/views/performance/database/useDenylistedProjects';
  11. import {useOutdatedSDKProjects} from 'sentry/views/performance/database/useOutdatedSDKProjects';
  12. interface Props {
  13. Wrapper?: React.ComponentType<any>;
  14. isDataAvailable?: boolean;
  15. }
  16. function DivWrapper(props) {
  17. return <div {...props} />;
  18. }
  19. export function NoDataMessage({Wrapper = DivWrapper, isDataAvailable}: Props) {
  20. const organization = useOrganization();
  21. const {selection, isReady: pageFilterIsReady} = usePageFilters();
  22. const selectedProjectIds = selection.projects.map(projectId => projectId.toString());
  23. const {projects: outdatedProjects, isFetching: areOutdatedProjectsFetching} =
  24. useOutdatedSDKProjects({
  25. projectId: selectedProjectIds,
  26. enabled: pageFilterIsReady && !isDataAvailable,
  27. });
  28. const {projects: denylistedProjects, isFetching: areDenylistProjectsFetching} =
  29. useDenylistedProjects({
  30. projectId: selectedProjectIds,
  31. });
  32. const isDataFetching = areOutdatedProjectsFetching || areDenylistProjectsFetching;
  33. if (isDataFetching) {
  34. return null;
  35. }
  36. const hasAnyProblematicProjects =
  37. (!areOutdatedProjectsFetching && outdatedProjects.length > 0) ||
  38. (!areDenylistProjectsFetching && denylistedProjects.length > 0);
  39. if (isDataAvailable && !hasAnyProblematicProjects) {
  40. return null;
  41. }
  42. return (
  43. <Wrapper>
  44. {!isDataAvailable &&
  45. tct(
  46. 'No queries found. Try updating your filters, or learn more about performance monitoring for queries in our [documentation:documentation].',
  47. {
  48. documentation: (
  49. <ExternalLink href="https://docs.sentry.io/product/performance/queries/" />
  50. ),
  51. }
  52. )}{' '}
  53. {outdatedProjects.length > 0 &&
  54. tct('You may be missing data due to outdated SDKs: [projectList].', {
  55. projectList: <ProjectList projects={outdatedProjects} />,
  56. })}{' '}
  57. {denylistedProjects.length > 0 &&
  58. tct(
  59. 'Some of your projects have been omitted from query performance analysis. Please [supportLink]. Omitted projects: [projectList].',
  60. {
  61. supportLink: (
  62. <Button priority="link" onClick={() => openHelpSearchModal({organization})}>
  63. {t('Contact Support')}
  64. </Button>
  65. ),
  66. projectList: <ProjectList projects={denylistedProjects} />,
  67. }
  68. )}
  69. </Wrapper>
  70. );
  71. }
  72. interface ProjectListProps {
  73. projects: Project[];
  74. limit?: number;
  75. }
  76. function ProjectList({projects, limit = MAX_LISTED_PROJECTS}: ProjectListProps) {
  77. const organization = useOrganization();
  78. const visibleProjects = projects.slice(0, limit + 1);
  79. const hasMoreProjectsThanVisible = projects.length > MAX_LISTED_PROJECTS;
  80. return (
  81. <Fragment>
  82. {visibleProjects.slice(0, limit).map((project, projectIndex) => {
  83. return (
  84. <span key={project.id}>
  85. <a
  86. href={normalizeUrl(
  87. `/organizations/${organization.slug}/projects/${project.slug}/`
  88. )}
  89. >
  90. {project.name}
  91. </a>
  92. {projectIndex < visibleProjects.length - 1 && ', '}
  93. </span>
  94. );
  95. })}
  96. {hasMoreProjectsThanVisible &&
  97. tct(' and [count] more.', {
  98. count: projects.length - limit,
  99. })}{' '}
  100. </Fragment>
  101. );
  102. }
  103. const MAX_LISTED_PROJECTS = 3;