index.tsx 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. // XXX: A lot of the UI for this file will be changed once we use IssueListActions
  2. // We're using GroupList to help us iterate quickly
  3. import type {RouteComponentProps} from 'react-router';
  4. import styled from '@emotion/styled';
  5. import Feature from 'sentry/components/acl/feature';
  6. import GroupList from 'sentry/components/issues/groupList';
  7. import * as Layout from 'sentry/components/layouts/thirds';
  8. import LoadingError from 'sentry/components/loadingError';
  9. import LoadingIndicator from 'sentry/components/loadingIndicator';
  10. import {t} from 'sentry/locale';
  11. import {space} from 'sentry/styles/space';
  12. import {useApiQuery} from 'sentry/utils/queryClient';
  13. import useOrganization from 'sentry/utils/useOrganization';
  14. type RouteParams = {
  15. groupId: string;
  16. };
  17. type Props = RouteComponentProps<RouteParams, {}>;
  18. type RelatedIssuesResponse = {
  19. data: [
  20. {
  21. data: number[];
  22. type: string;
  23. },
  24. ];
  25. };
  26. function GroupRelatedIssues({params}: Props) {
  27. const {groupId} = params;
  28. const organization = useOrganization();
  29. const orgSlug = organization.slug;
  30. // Fetch the list of related issues
  31. const {
  32. isLoading,
  33. isError,
  34. data: relatedIssues,
  35. refetch,
  36. } = useApiQuery<RelatedIssuesResponse>([`/issues/${groupId}/related-issues/`], {
  37. staleTime: 0,
  38. });
  39. const sameRootCauseIssues = relatedIssues?.data
  40. .filter(item => item.type === 'same_root_cause')
  41. .map(item => item.data);
  42. // If the group we're looking related issues for shows up in the table,
  43. // it will trigger a bug in getGroupReprocessingStatus because activites would be empty,
  44. // thus, we excude it from the list of related issues
  45. const groups = sameRootCauseIssues?.filter(id => id.toString() !== groupId)?.join(',');
  46. return (
  47. <Layout.Body>
  48. <Layout.Main fullWidth>
  49. <HeaderWrapper>
  50. <Title>{t('Related Issues')}</Title>
  51. <small>
  52. {t(
  53. 'Related Issues are issues that may have the same root cause and can be acted on together.'
  54. )}
  55. </small>
  56. </HeaderWrapper>
  57. {isLoading ? (
  58. <LoadingIndicator />
  59. ) : isError ? (
  60. <LoadingError
  61. message={t('Unable to load related issues, please try again later')}
  62. onRetry={refetch}
  63. />
  64. ) : groups ? (
  65. <GroupList
  66. endpointPath={`/organizations/${orgSlug}/issues/`}
  67. orgSlug={orgSlug}
  68. queryParams={{query: `issue.id:[${groups}]`}}
  69. query=""
  70. source="related-issues-tab"
  71. renderEmptyMessage={() => <Title>No related issues</Title>}
  72. renderErrorMessage={() => <Title>Error loading related issues</Title>}
  73. />
  74. ) : (
  75. <b>No related issues found!</b>
  76. )}
  77. </Layout.Main>
  78. </Layout.Body>
  79. );
  80. }
  81. function GroupRelatedIssuesWrapper(props: Props) {
  82. return (
  83. <Feature features={['related-issues']}>
  84. <GroupRelatedIssues {...props} />
  85. </Feature>
  86. );
  87. }
  88. // Export the component without feature flag controls
  89. export {GroupRelatedIssues};
  90. export default GroupRelatedIssuesWrapper;
  91. const Title = styled('h4')`
  92. margin-bottom: ${space(0.75)};
  93. `;
  94. const HeaderWrapper = styled('div')`
  95. margin-bottom: ${space(2)};
  96. small {
  97. color: ${p => p.theme.subText};
  98. }
  99. `;