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.

ProjectCardMeasures.tsx 7.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  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. CoverageIndicator,
  22. DuplicationsIndicator,
  23. MetricsLabel,
  24. MetricsRatingBadge,
  25. Note,
  26. PageContentFontWrapper,
  27. } from 'design-system';
  28. import * as React from 'react';
  29. import Measure from '../../../../components/measure/Measure';
  30. import { duplicationRatingConverter } from '../../../../components/measure/utils';
  31. import { translate } from '../../../../helpers/l10n';
  32. import { formatRating } from '../../../../helpers/measures';
  33. import { isDefined } from '../../../../helpers/types';
  34. import { ComponentQualifier } from '../../../../types/component';
  35. import { MetricKey, MetricType } from '../../../../types/metrics';
  36. import { Dict } from '../../../../types/types';
  37. import ProjectCardMeasure from './ProjectCardMeasure';
  38. export interface ProjectCardMeasuresProps {
  39. isNewCode: boolean;
  40. measures: Dict<string | undefined>;
  41. componentQualifier: ComponentQualifier;
  42. }
  43. function renderNewIssues(props: ProjectCardMeasuresProps) {
  44. const { measures, isNewCode } = props;
  45. if (!isNewCode) {
  46. return null;
  47. }
  48. return (
  49. <ProjectCardMeasure
  50. metricKey={MetricKey.new_violations}
  51. label={translate(`metric.${MetricKey.new_violations}.description`)}
  52. >
  53. <Measure
  54. metricKey={MetricKey.new_violations}
  55. metricType={MetricType.ShortInteger}
  56. value={measures[MetricKey.new_violations]}
  57. className="sw-ml-2 sw-body-md-highlight"
  58. />
  59. </ProjectCardMeasure>
  60. );
  61. }
  62. function renderCoverage(props: ProjectCardMeasuresProps) {
  63. const { measures, isNewCode } = props;
  64. const coverageMetric = isNewCode ? MetricKey.new_coverage : MetricKey.coverage;
  65. return (
  66. <ProjectCardMeasure metricKey={coverageMetric} label={translate('metric.coverage.name')}>
  67. <div>
  68. {measures[coverageMetric] && <CoverageIndicator value={measures[coverageMetric]} />}
  69. <Measure
  70. metricKey={coverageMetric}
  71. metricType={MetricType.Percent}
  72. value={measures[coverageMetric]}
  73. className="sw-ml-2 sw-body-md-highlight"
  74. />
  75. </div>
  76. </ProjectCardMeasure>
  77. );
  78. }
  79. function renderDuplication(props: ProjectCardMeasuresProps) {
  80. const { measures, isNewCode } = props;
  81. const duplicationMetric = isNewCode
  82. ? MetricKey.new_duplicated_lines_density
  83. : MetricKey.duplicated_lines_density;
  84. const rating =
  85. measures[duplicationMetric] !== undefined
  86. ? duplicationRatingConverter(Number(measures[duplicationMetric]))
  87. : undefined;
  88. return (
  89. <ProjectCardMeasure
  90. metricKey={duplicationMetric}
  91. label={translate('metric.duplicated_lines_density.short_name')}
  92. >
  93. <div>
  94. {measures[duplicationMetric] != null && <DuplicationsIndicator rating={rating} />}
  95. <Measure
  96. metricKey={duplicationMetric}
  97. metricType={MetricType.Percent}
  98. value={measures[duplicationMetric]}
  99. className="sw-ml-2 sw-body-md-highlight"
  100. />
  101. </div>
  102. </ProjectCardMeasure>
  103. );
  104. }
  105. function renderRatings(props: ProjectCardMeasuresProps) {
  106. const { isNewCode, measures } = props;
  107. const measuresByCodeLeak = isNewCode
  108. ? []
  109. : [
  110. {
  111. iconLabel: translate(`metric.${MetricKey.security_issues}.short_name`),
  112. noShrink: true,
  113. metricKey:
  114. measures[MetricKey.security_issues] !== undefined
  115. ? MetricKey.security_issues
  116. : MetricKey.vulnerabilities,
  117. metricRatingKey: MetricKey.security_rating,
  118. metricType: MetricType.ShortInteger,
  119. },
  120. {
  121. iconLabel: translate(`metric.${MetricKey.reliability_issues}.short_name`),
  122. metricKey:
  123. measures[MetricKey.reliability_issues] !== undefined
  124. ? MetricKey.reliability_issues
  125. : MetricKey.bugs,
  126. metricRatingKey: MetricKey.reliability_rating,
  127. metricType: MetricType.ShortInteger,
  128. },
  129. {
  130. iconLabel: translate(`metric.${MetricKey.maintainability_issues}.short_name`),
  131. metricKey:
  132. measures[MetricKey.maintainability_issues] !== undefined
  133. ? MetricKey.maintainability_issues
  134. : MetricKey.code_smells,
  135. metricRatingKey: MetricKey.sqale_rating,
  136. metricType: MetricType.ShortInteger,
  137. },
  138. ];
  139. const measureList = [
  140. ...measuresByCodeLeak,
  141. {
  142. iconKey: 'security_hotspots',
  143. iconLabel: translate('projects.security_hotspots_reviewed'),
  144. metricKey: isNewCode
  145. ? MetricKey.new_security_hotspots_reviewed
  146. : MetricKey.security_hotspots_reviewed,
  147. metricRatingKey: isNewCode
  148. ? MetricKey.new_security_review_rating
  149. : MetricKey.security_review_rating,
  150. metricType: MetricType.Percent,
  151. },
  152. ];
  153. return measureList.map((measure) => {
  154. const { iconLabel, metricKey, metricRatingKey, metricType } = measure;
  155. const value = formatRating(measures[metricRatingKey]);
  156. const measureValue =
  157. [
  158. MetricKey.security_issues,
  159. MetricKey.reliability_issues,
  160. MetricKey.maintainability_issues,
  161. ].includes(metricKey) && measures[metricKey]
  162. ? JSON.parse(measures[metricKey] as string)?.total
  163. : measures[metricKey];
  164. return (
  165. <ProjectCardMeasure key={metricKey} metricKey={metricKey} label={iconLabel}>
  166. <MetricsRatingBadge label={metricKey} rating={value as MetricsLabel} />
  167. <Measure
  168. metricKey={metricKey}
  169. metricType={metricType}
  170. value={measureValue}
  171. className="sw-ml-2 sw-body-md-highlight"
  172. />
  173. </ProjectCardMeasure>
  174. );
  175. });
  176. }
  177. export default function ProjectCardMeasures(props: ProjectCardMeasuresProps) {
  178. const { isNewCode, measures, componentQualifier } = props;
  179. const { ncloc } = measures;
  180. if (!isNewCode && !ncloc) {
  181. return (
  182. <Note className="sw-py-4">
  183. {componentQualifier === ComponentQualifier.Application
  184. ? translate('portfolio.app.empty')
  185. : translate('overview.project.main_branch_empty')}
  186. </Note>
  187. );
  188. }
  189. const measureList = [
  190. renderNewIssues(props),
  191. ...renderRatings(props),
  192. renderCoverage(props),
  193. renderDuplication(props),
  194. ].filter(isDefined);
  195. return (
  196. <PageContentFontWrapper className="sw-flex sw-gap-8">
  197. {measureList.map((measure, i) => (
  198. // eslint-disable-next-line react/no-array-index-key
  199. <React.Fragment key={i}>{measure}</React.Fragment>
  200. ))}
  201. </PageContentFontWrapper>
  202. );
  203. }