index.tsx 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. import {useEffect, useMemo} from 'react';
  2. import styled from '@emotion/styled';
  3. import GuideAnchor from 'sentry/components/assistant/guideAnchor';
  4. import * as Layout from 'sentry/components/layouts/thirds';
  5. import LoadingError from 'sentry/components/loadingError';
  6. import LoadingIndicator from 'sentry/components/loadingIndicator';
  7. import NoProjectMessage from 'sentry/components/noProjectMessage';
  8. import {EnvironmentPageFilter} from 'sentry/components/organizations/environmentPageFilter';
  9. import PageFilterBar from 'sentry/components/organizations/pageFilterBar';
  10. import PageFiltersContainer from 'sentry/components/organizations/pageFilters/container';
  11. import {ProjectPageFilter} from 'sentry/components/organizations/projectPageFilter';
  12. import PanelTable from 'sentry/components/panels/panelTable';
  13. // import {ALL_ACCESS_PROJECTS} from 'sentry/constants/pageFilters';
  14. import {t} from 'sentry/locale';
  15. // import ConfigStore from 'sentry/stores/configStore';
  16. import {space} from 'sentry/styles/space';
  17. // import {Project} from 'sentry/types';
  18. import useOrganization from 'sentry/utils/useOrganization';
  19. import usePageFilters from 'sentry/utils/usePageFilters';
  20. // import useProjects from 'sentry/utils/useProjects';
  21. import useRouter from 'sentry/utils/useRouter';
  22. import Header from '../components/header';
  23. import {Threshold} from '../utils/types';
  24. import useFetchThresholdsListData from '../utils/useFetchThresholdsListData';
  25. import {ThresholdGroupRow} from './thresholdGroupRow';
  26. type Props = {};
  27. function ReleaseThresholdList({}: Props) {
  28. const router = useRouter();
  29. const organization = useOrganization();
  30. useEffect(() => {
  31. const hasV2ReleaseUIEnabled = organization.features.includes('release-ui-v2');
  32. if (!hasV2ReleaseUIEnabled) {
  33. router.replace('/releases/');
  34. }
  35. }, [router, organization]);
  36. // const {projects} = useProjects();
  37. const {selection} = usePageFilters();
  38. const {
  39. data: thresholds = [],
  40. error,
  41. isLoading,
  42. isError,
  43. refetch,
  44. } = useFetchThresholdsListData({
  45. selectedProjectIds: selection.projects,
  46. });
  47. // const _getAllSelectedProjects = (): Project[] => {
  48. // return projects.filter(project =>
  49. // selection.projects.some(id => String(id) === project.id || id === -1)
  50. // );
  51. // };
  52. // const _getAllEnvironments = (): string[] => {
  53. // const selectedProjects = selection.projects.map(id => String(id));
  54. // const {user} = ConfigStore.getState();
  55. // const allEnvSet = new Set(projects.flatMap(project => project.environments));
  56. // // NOTE: mostly taken from environmentSelector.tsx
  57. // const unSortedEnvs = new Set(
  58. // projects.flatMap(project => {
  59. // /**
  60. // * Include environments from:
  61. // * all projects if the user is a superuser
  62. // * the requested projects
  63. // * all member projects if 'my projects' (empty list) is selected.
  64. // * all projects if -1 is the only selected project.
  65. // */
  66. // if (
  67. // (selectedProjects.length === 1 &&
  68. // selectedProjects[0] === ALL_ACCESS_PROJECTS &&
  69. // project.hasAccess) ||
  70. // (selectedProjects.length === 0 && (project.isMember || user.isSuperuser)) ||
  71. // selectedProjects.includes(project.id)
  72. // ) {
  73. // return project.environments;
  74. // }
  75. // return [];
  76. // })
  77. // );
  78. // const envDiff = new Set([...allEnvSet].filter(x => !unSortedEnvs.has(x)));
  79. // return Array.from(unSortedEnvs)
  80. // .sort()
  81. // .concat([...envDiff].sort());
  82. // };
  83. // NOTE: currently no way to filter for 'None' environments
  84. const filteredThresholds = selection.environments.length
  85. ? thresholds.filter(
  86. threshold => selection.environments.indexOf(threshold.environment.name) > -1
  87. )
  88. : thresholds;
  89. const thresholdGroups: {[key: string]: {[key: string]: Threshold[]}} = useMemo(() => {
  90. const byProj = {};
  91. filteredThresholds.forEach(threshold => {
  92. const projId = threshold.project.id;
  93. if (!byProj[projId]) {
  94. byProj[projId] = {};
  95. }
  96. const env = threshold.environment.name;
  97. if (!byProj[projId][env]) {
  98. byProj[projId][env] = [];
  99. }
  100. byProj[projId][env].push(threshold);
  101. });
  102. return byProj;
  103. }, [filteredThresholds]);
  104. if (isError) {
  105. return <LoadingError onRetry={refetch} message={error.message} />;
  106. }
  107. if (isLoading) {
  108. return <LoadingIndicator />;
  109. }
  110. return (
  111. <PageFiltersContainer>
  112. <NoProjectMessage organization={organization}>
  113. <Header router={router} hasV2ReleaseUIEnabled />
  114. <Layout.Body>
  115. <Layout.Main fullWidth>
  116. <ReleaseThresholdsPageFilterBar condensed>
  117. <GuideAnchor target="release_projects">
  118. <ProjectPageFilter />
  119. </GuideAnchor>
  120. <EnvironmentPageFilter />
  121. </ReleaseThresholdsPageFilterBar>
  122. <StyledPanelTable
  123. isLoading={isLoading}
  124. isEmpty={filteredThresholds.length === 0 && !isError}
  125. emptyMessage={t('No thresholds found.')}
  126. headers={[
  127. t('Project Name'),
  128. t('Environment'),
  129. t('Window'),
  130. t('Conditions'),
  131. t('Actions'),
  132. ]}
  133. >
  134. {thresholdGroups &&
  135. Object.entries(thresholdGroups).map(([projId, byEnv]) => {
  136. return Object.entries(byEnv).map(([envName, thresholdGroup]) => (
  137. <ThresholdGroupRow
  138. key={`${projId}-${envName}`}
  139. thresholds={thresholdGroup}
  140. />
  141. ));
  142. })}
  143. </StyledPanelTable>
  144. </Layout.Main>
  145. </Layout.Body>
  146. </NoProjectMessage>
  147. </PageFiltersContainer>
  148. );
  149. }
  150. export default ReleaseThresholdList;
  151. const StyledPanelTable = styled(PanelTable)`
  152. @media (min-width: ${p => p.theme.breakpoints.small}) {
  153. overflow: initial;
  154. }
  155. grid-template-columns:
  156. minmax(150px, 1fr) minmax(150px, 1fr) minmax(150px, 1fr) minmax(250px, 4fr)
  157. auto;
  158. white-space: nowrap;
  159. font-size: ${p => p.theme.fontSizeMedium};
  160. `;
  161. const ReleaseThresholdsPageFilterBar = styled(PageFilterBar)`
  162. margin-bottom: ${space(2)};
  163. `;