issueList.tsx 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. import {Fragment, useCallback, useEffect, useMemo, useState} from 'react';
  2. // eslint-disable-next-line no-restricted-imports
  3. import {withRouter, WithRouterProps} from 'react-router';
  4. import CompactIssue from 'sentry/components/issues/compactIssue';
  5. import LoadingError from 'sentry/components/loadingError';
  6. import LoadingIndicator from 'sentry/components/loadingIndicator';
  7. import Pagination from 'sentry/components/pagination';
  8. import {Panel, PanelBody} from 'sentry/components/panels';
  9. import {IconSearch} from 'sentry/icons';
  10. import {t} from 'sentry/locale';
  11. import space from 'sentry/styles/space';
  12. import {Group} from 'sentry/types';
  13. import useApi from 'sentry/utils/useApi';
  14. import EmptyMessage from 'sentry/views/settings/components/emptyMessage';
  15. interface IssueListProps extends WithRouterProps {
  16. endpoint: string;
  17. emptyText?: string;
  18. noBorder?: boolean;
  19. noMargin?: boolean;
  20. pagination?: boolean;
  21. query?: Record<string, any>;
  22. renderEmpty?: () => React.ReactElement;
  23. }
  24. interface IssueListState {
  25. data: Array<Group>;
  26. issueIds: Array<string>;
  27. pageLinks: string | null;
  28. status: 'loading' | 'error' | 'success';
  29. }
  30. function IssueList({
  31. endpoint,
  32. emptyText,
  33. query,
  34. location,
  35. pagination,
  36. renderEmpty,
  37. noBorder,
  38. noMargin,
  39. }: IssueListProps): React.ReactElement {
  40. const api = useApi();
  41. const [state, setState] = useState<IssueListState>({
  42. issueIds: [],
  43. status: 'loading',
  44. pageLinks: null,
  45. data: [],
  46. });
  47. const fetchIssueListData = useCallback(() => {
  48. api.clear();
  49. api.request(endpoint, {
  50. method: 'GET',
  51. query: {
  52. ...query,
  53. ...(location?.query?.cursor ? {cursor: location.query.cursor} : {}),
  54. },
  55. success: (data, _, resp) => {
  56. setState({
  57. data,
  58. status: 'success',
  59. issueIds: data.map(item => item.id),
  60. pageLinks: resp?.getResponseHeader('Link') ?? null,
  61. });
  62. },
  63. error: () => {
  64. setState(prevState => ({...prevState, status: 'error'}));
  65. },
  66. });
  67. }, [query, endpoint, location.query, api]);
  68. // TODO: location should always be passed as a prop, check why we have this
  69. const hasLocation = !!location;
  70. useEffect(() => {
  71. if (!hasLocation) {
  72. return;
  73. }
  74. setState({issueIds: [], status: 'loading', pageLinks: null, data: []});
  75. fetchIssueListData();
  76. }, [fetchIssueListData, hasLocation]);
  77. const panelStyles = useMemo(() => {
  78. const styles: React.CSSProperties = {
  79. ...(noBorder ? {border: 0, borderRadius: 0} : {}),
  80. ...(noMargin ? {marginBottom: 0} : {}),
  81. };
  82. return styles;
  83. }, [noBorder, noMargin]);
  84. return (
  85. <Fragment>
  86. {state.status === 'loading' ? (
  87. <div style={{margin: '18px 18px 0'}}>
  88. <LoadingIndicator />
  89. </div>
  90. ) : state.status === 'error' ? (
  91. <div style={{margin: `${space(2)} ${space(2)} 0`}}>
  92. <LoadingError onRetry={fetchIssueListData} />
  93. </div>
  94. ) : state.issueIds.length > 0 ? (
  95. <Panel style={panelStyles}>
  96. <PanelBody className="issue-list">
  97. {state.data.map(issue => (
  98. <CompactIssue key={issue.id} id={issue.id} data={issue} />
  99. ))}
  100. </PanelBody>
  101. </Panel>
  102. ) : renderEmpty ? (
  103. renderEmpty()
  104. ) : (
  105. <Panel style={panelStyles}>
  106. <EmptyMessage icon={<IconSearch size="xl" />}>
  107. {emptyText ?? t('Nothing to show here, move along.')}
  108. </EmptyMessage>
  109. </Panel>
  110. )}
  111. {pagination && state.pageLinks && <Pagination pageLinks={state.pageLinks} />}
  112. </Fragment>
  113. );
  114. }
  115. export {IssueList};
  116. export default withRouter(IssueList);