index.tsx 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  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. same_root_cause: number[];
  20. };
  21. function GroupRelatedIssues({params}: Props) {
  22. const {groupId} = params;
  23. const organization = useOrganization();
  24. const orgSlug = organization.slug;
  25. // Fetch the list of related issues
  26. const {
  27. isLoading,
  28. isError,
  29. data: relatedIssues,
  30. refetch,
  31. } = useApiQuery<RelatedIssuesResponse>([`/issues/${groupId}/related-issues/`], {
  32. staleTime: 0,
  33. });
  34. // If the group we're looking related issues for shows up in the table,
  35. // it will trigger a bug in getGroupReprocessingStatus because activites would be empty
  36. const groups = relatedIssues?.same_root_cause
  37. ?.filter(id => id.toString() !== groupId)
  38. ?.join(',');
  39. return (
  40. <Layout.Body>
  41. <Layout.Main fullWidth>
  42. <HeaderWrapper>
  43. <Title>{t('Related Issues')}</Title>
  44. <small>
  45. {t(
  46. 'Related Issues are issues that may have the same root cause and can be acted on together.'
  47. )}
  48. </small>
  49. </HeaderWrapper>
  50. {isLoading ? (
  51. <LoadingIndicator />
  52. ) : isError ? (
  53. <LoadingError
  54. message={t('Unable to load related issues, please try again later')}
  55. onRetry={refetch}
  56. />
  57. ) : groups ? (
  58. <GroupList
  59. endpointPath={`/organizations/${orgSlug}/issues/`}
  60. orgSlug={orgSlug}
  61. queryParams={{query: `issue.id:[${groups}]`}}
  62. query=""
  63. source="related-issues-tab"
  64. renderEmptyMessage={() => <Title>No related issues</Title>}
  65. renderErrorMessage={() => <Title>Error loading related issues</Title>}
  66. />
  67. ) : (
  68. <b>No related issues found!</b>
  69. )}
  70. </Layout.Main>
  71. </Layout.Body>
  72. );
  73. }
  74. function GroupRelatedIssuesWrapper(props: Props) {
  75. return (
  76. <Feature features={['related-issues']}>
  77. <GroupRelatedIssues {...props} />
  78. </Feature>
  79. );
  80. }
  81. // Export the component without feature flag controls
  82. export {GroupRelatedIssues};
  83. export default GroupRelatedIssuesWrapper;
  84. const Title = styled('h4')`
  85. margin-bottom: ${space(0.75)};
  86. `;
  87. const HeaderWrapper = styled('div')`
  88. margin-bottom: ${space(2)};
  89. small {
  90. color: ${p => p.theme.subText};
  91. }
  92. `;