issueList.tsx 3.6 KB

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