You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

BranchOverviewRenderer.tsx 8.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2024 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. import {
  21. BasicSeparator,
  22. LargeCenteredLayout,
  23. LightGreyCard,
  24. LightGreyCardTitle,
  25. PageContentFontWrapper,
  26. } from 'design-system';
  27. import * as React from 'react';
  28. import A11ySkipTarget from '../../../components/a11y/A11ySkipTarget';
  29. import { useLocation, useRouter } from '../../../components/hoc/withRouter';
  30. import AnalysisMissingInfoMessage from '../../../components/shared/AnalysisMissingInfoMessage';
  31. import { parseDate } from '../../../helpers/dates';
  32. import { areCCTMeasuresComputed, isDiffMetric } from '../../../helpers/measures';
  33. import { CodeScope } from '../../../helpers/urls';
  34. import { ApplicationPeriod } from '../../../types/application';
  35. import { Branch } from '../../../types/branch-like';
  36. import { ComponentQualifier, isPortfolioLike } from '../../../types/component';
  37. import { Analysis, GraphType, MeasureHistory } from '../../../types/project-activity';
  38. import { QualityGateStatus } from '../../../types/quality-gates';
  39. import { Component, MeasureEnhanced, Metric, Period, QualityGate } from '../../../types/types';
  40. import { AnalysisStatus } from '../components/AnalysisStatus';
  41. import LastAnalysisLabel from '../components/LastAnalysisLabel';
  42. import ActivityPanel from './ActivityPanel';
  43. import BranchMetaTopBar from './BranchMetaTopBar';
  44. import FirstAnalysisNextStepsNotif from './FirstAnalysisNextStepsNotif';
  45. import MeasuresPanelNoNewCode from './MeasuresPanelNoNewCode';
  46. import NewCodeMeasuresPanel from './NewCodeMeasuresPanel';
  47. import NoCodeWarning from './NoCodeWarning';
  48. import OverallCodeMeasuresPanel from './OverallCodeMeasuresPanel';
  49. import QualityGatePanel from './QualityGatePanel';
  50. import { QualityGateStatusTitle } from './QualityGateStatusTitle';
  51. import SonarLintPromotion from './SonarLintPromotion';
  52. import { TabsPanel } from './TabsPanel';
  53. export interface BranchOverviewRendererProps {
  54. analyses?: Analysis[];
  55. appLeak?: ApplicationPeriod;
  56. branch?: Branch;
  57. branchesEnabled?: boolean;
  58. component: Component;
  59. detectedCIOnLastAnalysis?: boolean;
  60. graph?: GraphType;
  61. loadingHistory?: boolean;
  62. loadingStatus?: boolean;
  63. measures?: MeasureEnhanced[];
  64. measuresHistory?: MeasureHistory[];
  65. metrics?: Metric[];
  66. onGraphChange: (graph: GraphType) => void;
  67. period?: Period;
  68. projectIsEmpty?: boolean;
  69. qgStatuses?: QualityGateStatus[];
  70. qualityGate?: QualityGate;
  71. }
  72. export default function BranchOverviewRenderer(props: BranchOverviewRendererProps) {
  73. const {
  74. analyses,
  75. appLeak,
  76. branch,
  77. branchesEnabled,
  78. component,
  79. detectedCIOnLastAnalysis,
  80. graph,
  81. loadingHistory,
  82. loadingStatus,
  83. measures = [],
  84. measuresHistory = [],
  85. metrics = [],
  86. onGraphChange,
  87. period,
  88. projectIsEmpty,
  89. qgStatuses,
  90. qualityGate,
  91. } = props;
  92. const { query } = useLocation();
  93. const router = useRouter();
  94. const tab = query.codeScope === CodeScope.Overall ? CodeScope.Overall : CodeScope.New;
  95. const leakPeriod = component.qualifier === ComponentQualifier.Application ? appLeak : period;
  96. const isNewCodeTab = tab === CodeScope.New;
  97. const hasNewCodeMeasures = measures.some((m) => isDiffMetric(m.metric.key));
  98. // Check if any potentially missing uncomputed measure is not present
  99. const isMissingMeasures = !areCCTMeasuresComputed(measures);
  100. const selectTab = (tab: CodeScope) => {
  101. router.replace({ query: { ...query, codeScope: tab } });
  102. };
  103. React.useEffect(() => {
  104. // Open Overall tab by default if there are no new measures.
  105. if (loadingStatus === false && !hasNewCodeMeasures && isNewCodeTab) {
  106. selectTab(CodeScope.Overall);
  107. }
  108. // In this case, we explicitly do NOT want to mark tab as a dependency, as
  109. // it would prevent the user from selecting it, even if it's empty.
  110. /* eslint-disable-next-line react-hooks/exhaustive-deps */
  111. }, [loadingStatus, hasNewCodeMeasures]);
  112. const analysisMissingInfo = isMissingMeasures && (
  113. <AnalysisMissingInfoMessage
  114. qualifier={component.qualifier}
  115. hide={isPortfolioLike(component.qualifier)}
  116. className="sw-mt-6"
  117. />
  118. );
  119. return (
  120. <>
  121. <FirstAnalysisNextStepsNotif
  122. component={component}
  123. branchesEnabled={branchesEnabled}
  124. detectedCIOnLastAnalysis={detectedCIOnLastAnalysis}
  125. />
  126. <LargeCenteredLayout>
  127. <PageContentFontWrapper>
  128. <div className="overview sw-my-6 sw-body-sm">
  129. <A11ySkipTarget anchor="overview_main" />
  130. {projectIsEmpty ? (
  131. <NoCodeWarning branchLike={branch} component={component} measures={measures} />
  132. ) : (
  133. <div>
  134. {branch && (
  135. <>
  136. <BranchMetaTopBar branch={branch} component={component} measures={measures} />
  137. <BasicSeparator />
  138. </>
  139. )}
  140. <AnalysisStatus className="sw-mt-6" component={component} />
  141. <div className="sw-flex sw-gap-3 sw-mt-6">
  142. <div className="sw-w-1/4">
  143. <LightGreyCard>
  144. <QualityGateStatusTitle />
  145. <QualityGatePanel
  146. component={component}
  147. loading={loadingStatus}
  148. qgStatuses={qgStatuses}
  149. qualityGate={qualityGate}
  150. />
  151. </LightGreyCard>
  152. <SonarLintPromotion
  153. qgConditions={qgStatuses?.flatMap((qg) => qg.failedConditions)}
  154. />
  155. </div>
  156. <div className="sw-flex-1">
  157. <LightGreyCard className="sw-flex sw-flex-col">
  158. <LightGreyCardTitle>
  159. <div>&nbsp;</div>
  160. <LastAnalysisLabel analysisDate={branch?.analysisDate} />
  161. </LightGreyCardTitle>
  162. <TabsPanel
  163. analyses={analyses}
  164. appLeak={appLeak}
  165. component={component}
  166. loading={loadingStatus}
  167. period={period}
  168. qgStatuses={qgStatuses}
  169. isNewCode={isNewCodeTab}
  170. onTabSelect={selectTab}
  171. >
  172. {isNewCodeTab && (
  173. <>
  174. {hasNewCodeMeasures ? (
  175. <NewCodeMeasuresPanel
  176. qgStatuses={qgStatuses}
  177. branch={branch}
  178. component={component}
  179. measures={measures}
  180. />
  181. ) : (
  182. <MeasuresPanelNoNewCode
  183. branch={branch}
  184. component={component}
  185. period={period}
  186. />
  187. )}
  188. </>
  189. )}
  190. {!isNewCodeTab && (
  191. <>
  192. {analysisMissingInfo}
  193. <OverallCodeMeasuresPanel
  194. branch={branch}
  195. qgStatuses={qgStatuses}
  196. component={component}
  197. measures={measures}
  198. />
  199. </>
  200. )}
  201. </TabsPanel>
  202. <ActivityPanel
  203. analyses={analyses}
  204. branchLike={branch}
  205. component={component}
  206. graph={graph}
  207. leakPeriodDate={leakPeriod && parseDate(leakPeriod.date)}
  208. loading={loadingHistory}
  209. measuresHistory={measuresHistory}
  210. metrics={metrics}
  211. onGraphChange={onGraphChange}
  212. />
  213. </LightGreyCard>
  214. </div>
  215. </div>
  216. </div>
  217. )}
  218. </div>
  219. </PageContentFontWrapper>
  220. </LargeCenteredLayout>
  221. </>
  222. );
  223. }