index.tsx 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. import {RouteComponentProps} from 'react-router';
  2. import EmptyStateWarning from 'sentry/components/emptyStateWarning';
  3. import ErrorBoundary from 'sentry/components/errorBoundary';
  4. import * as Layout from 'sentry/components/layouts/thirds';
  5. import LoadingIndicator from 'sentry/components/loadingIndicator';
  6. import Pagination from 'sentry/components/pagination';
  7. import {Panel} from 'sentry/components/panels';
  8. import {t} from 'sentry/locale';
  9. import {space} from 'sentry/styles/space';
  10. import {Activity, Organization} from 'sentry/types';
  11. import routeTitle from 'sentry/utils/routeTitle';
  12. import withOrganization from 'sentry/utils/withOrganization';
  13. import AsyncView from 'sentry/views/asyncView';
  14. import ActivityFeedItem from './activityFeedItem';
  15. type Props = {
  16. organization: Organization;
  17. } & RouteComponentProps<{}, {}> &
  18. AsyncView['props'];
  19. type State = {
  20. activity: Activity[];
  21. } & AsyncView['state'];
  22. class OrganizationActivity extends AsyncView<Props, State> {
  23. getTitle() {
  24. const {organization} = this.props;
  25. return routeTitle(t('Activity'), organization.slug, false);
  26. }
  27. getEndpoints(): ReturnType<AsyncView['getEndpoints']> {
  28. const {organization} = this.props;
  29. return [['activity', `/organizations/${organization.slug}/activity/`]];
  30. }
  31. renderLoading() {
  32. return this.renderBody();
  33. }
  34. renderEmpty() {
  35. return (
  36. <EmptyStateWarning>
  37. <p>{t('Nothing to show here, move along.')}</p>
  38. </EmptyStateWarning>
  39. );
  40. }
  41. renderError(error?: Error, disableLog = false): React.ReactNode {
  42. const {errors} = this.state;
  43. const notFound = Object.values(errors).find(resp => resp && resp.status === 404);
  44. if (notFound) {
  45. return this.renderBody();
  46. }
  47. return super.renderError(error, disableLog);
  48. }
  49. renderBody() {
  50. const {loading, activity, activityPageLinks} = this.state;
  51. return (
  52. <Layout.Page>
  53. <Layout.Header>
  54. <Layout.HeaderContent>
  55. <Layout.Title>{t('Activity')}</Layout.Title>
  56. </Layout.HeaderContent>
  57. </Layout.Header>
  58. <Layout.Body>
  59. <Layout.Main fullWidth>
  60. <Panel>
  61. {loading && <LoadingIndicator />}
  62. {!loading && !activity?.length && this.renderEmpty()}
  63. {!loading && activity?.length > 0 && (
  64. <div data-test-id="activity-feed-list">
  65. {activity.map(item => (
  66. <ErrorBoundary
  67. mini
  68. css={{marginBottom: space(1), borderRadius: 0}}
  69. key={item.id}
  70. >
  71. <ActivityFeedItem
  72. organization={this.props.organization}
  73. item={item}
  74. />
  75. </ErrorBoundary>
  76. ))}
  77. </div>
  78. )}
  79. </Panel>
  80. {activityPageLinks && (
  81. <Pagination pageLinks={activityPageLinks} {...this.props} />
  82. )}
  83. </Layout.Main>
  84. </Layout.Body>
  85. </Layout.Page>
  86. );
  87. }
  88. }
  89. export default withOrganization(OrganizationActivity);