aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRevanshu Paliwal <revanshu.paliwal@sonarsource.com>2024-06-14 17:06:14 +0200
committersonartech <sonartech@sonarsource.com>2024-06-18 20:02:41 +0000
commit50ba3a3998f109b8540cd9485cbc19b160e85d59 (patch)
tree0137930b4746fb6848d9f5bade04f40e95603b18
parent5ff3751aa69b21ac0269eb9bc1b54625d42ecc08 (diff)
downloadsonarqube-50ba3a3998f109b8540cd9485cbc19b160e85d59.tar.gz
sonarqube-50ba3a3998f109b8540cd9485cbc19b160e85d59.zip
SONAR-22390 Refactor PR summary to similar to overview
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/branch-like/CurrentBranchLikeMergeInformation.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/BranchSummaryStyles.tsx15
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/FailedConditions.tsx16
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/QualityGatePanel.tsx8
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/QualityGatePanelSection.tsx8
-rw-r--r--server/sonar-web/src/main/js/apps/overview/pullRequests/IssueMeasuresCard.tsx173
-rw-r--r--server/sonar-web/src/main/js/apps/overview/pullRequests/MeasuresCardPanel.tsx220
-rw-r--r--server/sonar-web/src/main/js/apps/overview/pullRequests/PullRequestOverview.tsx5
8 files changed, 223 insertions, 224 deletions
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/branch-like/CurrentBranchLikeMergeInformation.tsx b/server/sonar-web/src/main/js/app/components/nav/component/branch-like/CurrentBranchLikeMergeInformation.tsx
index d8d7c24da37..def592a6601 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/branch-like/CurrentBranchLikeMergeInformation.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/component/branch-like/CurrentBranchLikeMergeInformation.tsx
@@ -31,7 +31,7 @@ export function CurrentBranchLikeMergeInformation({
}: Readonly<CurrentBranchLikeMergeInformationProps>) {
return (
<span
- className="sw-w-[400px] sw-text-ellipsis sw-whitespace-nowrap sw-overflow-hidden sw-flex-shrink sw-min-w-0"
+ className="sw-max-w-[400px] sw-text-ellipsis sw-whitespace-nowrap sw-overflow-hidden sw-flex-shrink sw-min-w-0"
title={translateWithParameters(
'branch_like_navigation.for_merge_into_x_from_y.title',
pullRequest.target,
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/BranchSummaryStyles.tsx b/server/sonar-web/src/main/js/apps/overview/branches/BranchSummaryStyles.tsx
index 82cfda5f355..0d4095baf8c 100644
--- a/server/sonar-web/src/main/js/apps/overview/branches/BranchSummaryStyles.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/branches/BranchSummaryStyles.tsx
@@ -54,6 +54,21 @@ export const StyleMeasuresCard = styled.div`
}
`;
+export const StyleMeasuresCardRightBorder = styled.div`
+ box-sizing: border-box;
+ position: relative;
+
+ &:not(:last-child):before {
+ content: '';
+ position: absolute;
+ top: 0;
+ right: calc(var(--grids-gaps) / -2);
+ height: 100%;
+ width: 1px;
+ background: ${themeColor('pageBlockBorder')};
+ }
+`;
+
export const StyledConditionsCard = styled.div`
box-sizing: border-box;
position: relative;
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/FailedConditions.tsx b/server/sonar-web/src/main/js/apps/overview/branches/FailedConditions.tsx
index d81e6250b12..223cbf1f836 100644
--- a/server/sonar-web/src/main/js/apps/overview/branches/FailedConditions.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/branches/FailedConditions.tsx
@@ -22,15 +22,18 @@ import _ from 'lodash';
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import { isDiffMetric } from '../../../helpers/measures';
-import { QualityGateStatus } from '../../../types/quality-gates';
-import { QualityGate } from '../../../types/types';
+import { BranchLike } from '../../../types/branch-like';
+import { QualityGateStatusConditionEnhanced } from '../../../types/quality-gates';
+import { Component, QualityGate } from '../../../types/types';
import ZeroNewIssuesSimplificationGuide from '../components/ZeroNewIssuesSimplificationGuide';
import QualityGateConditions from './QualityGateConditions';
export interface FailedConditionsProps {
+ branchLike?: BranchLike;
+ component: Pick<Component, 'key'>;
+ failedConditions: QualityGateStatusConditionEnhanced[];
isApplication?: boolean;
isNewCode: boolean;
- qgStatus: QualityGateStatus;
qualityGate?: QualityGate;
}
@@ -38,9 +41,10 @@ export default function FailedConditions({
isApplication,
isNewCode,
qualityGate,
- qgStatus,
+ failedConditions,
+ component,
+ branchLike,
}: FailedConditionsProps) {
- const { failedConditions, branchLike } = qgStatus;
const [newCodeFailedConditions, overallFailedConditions] = _.partition(
failedConditions,
(condition) => isDiffMetric(condition.metric),
@@ -70,7 +74,7 @@ export default function FailedConditions({
<ZeroNewIssuesSimplificationGuide qualityGate={qualityGate} />
)}
<QualityGateConditions
- component={qgStatus}
+ component={component}
branchLike={branchLike}
failedConditions={isNewCode ? newCodeFailedConditions : overallFailedConditions}
isBuiltInQualityGate={isNewCode && qualityGate?.isBuiltIn}
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/QualityGatePanel.tsx b/server/sonar-web/src/main/js/apps/overview/branches/QualityGatePanel.tsx
index 985cfa72f48..fb1dd98d321 100644
--- a/server/sonar-web/src/main/js/apps/overview/branches/QualityGatePanel.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/branches/QualityGatePanel.tsx
@@ -38,8 +38,8 @@ export interface QualityGatePanelProps {
loading?: boolean;
qgStatuses?: QualityGateStatus[];
qualityGate?: QualityGate;
- showCaycWarningInApp: boolean;
- showCaycWarningInProject: boolean;
+ showCaycWarningInApp?: boolean;
+ showCaycWarningInProject?: boolean;
totalFailedConditionLength: number;
}
@@ -51,8 +51,8 @@ export function QualityGatePanel(props: QualityGatePanelProps) {
qualityGate,
isNewCode = false,
totalFailedConditionLength,
- showCaycWarningInProject,
- showCaycWarningInApp,
+ showCaycWarningInProject = false,
+ showCaycWarningInApp = false,
} = props;
if (qgStatuses === undefined) {
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/QualityGatePanelSection.tsx b/server/sonar-web/src/main/js/apps/overview/branches/QualityGatePanelSection.tsx
index df91989c4a8..64993e86a8f 100644
--- a/server/sonar-web/src/main/js/apps/overview/branches/QualityGatePanelSection.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/branches/QualityGatePanelSection.tsx
@@ -64,7 +64,9 @@ export function QualityGatePanelSection(props: QualityGatePanelSectionProps) {
isNewCode={isNewCode}
isApplication={isApplication}
qualityGate={qualityGate}
- qgStatus={qgStatus}
+ failedConditions={qgStatus.failedConditions}
+ branchLike={qgStatus.branchLike}
+ component={qgStatus}
/>
</BorderlessAccordion>
@@ -75,7 +77,9 @@ export function QualityGatePanelSection(props: QualityGatePanelSectionProps) {
isNewCode={isNewCode}
isApplication={isApplication}
qualityGate={qualityGate}
- qgStatus={qgStatus}
+ failedConditions={qgStatus.failedConditions}
+ branchLike={qgStatus.branchLike}
+ component={qgStatus}
/>
)}
</>
diff --git a/server/sonar-web/src/main/js/apps/overview/pullRequests/IssueMeasuresCard.tsx b/server/sonar-web/src/main/js/apps/overview/pullRequests/IssueMeasuresCard.tsx
deleted file mode 100644
index 7f77a7d81a1..00000000000
--- a/server/sonar-web/src/main/js/apps/overview/pullRequests/IssueMeasuresCard.tsx
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import styled from '@emotion/styled';
-import {
- HelperHintIcon,
- LightGreyCard,
- LightLabel,
- SnoozeCircleIcon,
- TextError,
- TextSubdued,
- TrendDownCircleIcon,
- TrendUpCircleIcon,
- themeColor,
-} from 'design-system';
-import * as React from 'react';
-import { useIntl } from 'react-intl';
-import { getBranchLikeQuery } from '~sonar-aligned/helpers/branch-like';
-import { formatMeasure } from '~sonar-aligned/helpers/measures';
-import { getComponentIssuesUrl } from '~sonar-aligned/helpers/urls';
-import { MetricKey, MetricType } from '~sonar-aligned/types/metrics';
-import Tooltip from '../../../components/controls/Tooltip';
-import { getLeakValue } from '../../../components/measure/utils';
-import { DEFAULT_ISSUES_QUERY } from '../../../components/shared/utils';
-import { findMeasure } from '../../../helpers/measures';
-import { PullRequest } from '../../../types/branch-like';
-import { QualityGateStatusConditionEnhanced } from '../../../types/quality-gates';
-import { Component, MeasureEnhanced } from '../../../types/types';
-import { IssueMeasuresCardInner } from '../components/IssueMeasuresCardInner';
-import { Status, getConditionRequiredLabel } from '../utils';
-
-interface Props {
- component: Component;
- conditions: QualityGateStatusConditionEnhanced[];
- measures: MeasureEnhanced[];
- pullRequest: PullRequest;
-}
-
-export default function IssueMeasuresCard(
- props: React.PropsWithChildren<Props & React.HTMLAttributes<HTMLDivElement>>,
-) {
- const { measures, conditions, component, pullRequest, ...rest } = props;
-
- const intl = useIntl();
-
- const issuesCount = getLeakValue(findMeasure(measures, MetricKey.new_violations));
- const issuesCondition = conditions.find((c) => c.metric === MetricKey.new_violations);
- const issuesConditionFailed = issuesCondition?.level === Status.ERROR;
- const fixedCount = findMeasure(measures, MetricKey.pull_request_fixed_issues)?.value;
- const acceptedCount = getLeakValue(findMeasure(measures, MetricKey.new_accepted_issues));
-
- const issuesUrl = getComponentIssuesUrl(component.key, {
- ...getBranchLikeQuery(pullRequest),
- ...DEFAULT_ISSUES_QUERY,
- });
- const fixedUrl = getComponentIssuesUrl(component.key, {
- fixedInPullRequest: pullRequest.key,
- });
- const acceptedUrl = getComponentIssuesUrl(component.key, {
- ...getBranchLikeQuery(pullRequest),
- ...DEFAULT_ISSUES_QUERY,
- issueStatuses: 'ACCEPTED',
- });
-
- return (
- <LightGreyCard className="sw-p-8 sw-rounded-2 sw-flex sw-text-base sw-gap-4" {...rest}>
- <IssueMeasuresCardInner
- className="sw-w-1/3"
- header={intl.formatMessage({ id: 'overview.new_issues' })}
- data-testid={`overview__measures-${MetricKey.new_violations}`}
- data-guiding-id={issuesConditionFailed ? 'overviewZeroNewIssuesSimplification' : undefined}
- metric={MetricKey.new_violations}
- value={formatMeasure(issuesCount, MetricType.ShortInteger)}
- url={issuesUrl}
- failed={issuesConditionFailed}
- icon={issuesConditionFailed && <TrendUpCircleIcon />}
- footer={
- issuesCondition &&
- (issuesConditionFailed ? (
- <TextError
- className="sw-font-regular sw-body-xs sw-inline"
- text={getConditionRequiredLabel(issuesCondition, intl, true)}
- />
- ) : (
- <LightLabel className="sw-body-xs">
- {getConditionRequiredLabel(issuesCondition, intl)}
- </LightLabel>
- ))
- }
- />
- <StyledCardSeparator />
- <IssueMeasuresCardInner
- className="sw-w-1/3"
- header={intl.formatMessage({ id: 'overview.accepted_issues' })}
- data-testid={`overview__measures-${MetricKey.new_accepted_issues}`}
- metric={MetricKey.new_accepted_issues}
- value={formatMeasure(acceptedCount, MetricType.ShortInteger)}
- disabled={component.needIssueSync}
- url={acceptedUrl}
- icon={
- acceptedCount && (
- <SnoozeCircleIcon
- color={acceptedCount === '0' ? 'overviewCardDefaultIcon' : 'overviewCardWarningIcon'}
- />
- )
- }
- footer={
- <TextSubdued className="sw-body-xs">
- {intl.formatMessage({ id: 'overview.accepted_issues.help' })}
- </TextSubdued>
- }
- />
- <StyledCardSeparator />
- <IssueMeasuresCardInner
- className="sw-w-1/3"
- header={
- <>
- {intl.formatMessage({ id: 'overview.pull_request.fixed_issues' })}
- <Tooltip
- content={
- <div className="sw-flex sw-flex-col sw-gap-4">
- <span>
- {intl.formatMessage({ id: 'overview.pull_request.fixed_issues.disclaimer' })}
- </span>
- <span>
- {intl.formatMessage({
- id: 'overview.pull_request.fixed_issues.disclaimer.2',
- })}
- </span>
- </div>
- }
- side="top"
- >
- <HelperHintIcon raised />
- </Tooltip>
- </>
- }
- data-testid={`overview__measures-${MetricKey.pull_request_fixed_issues}`}
- metric={MetricKey.pull_request_fixed_issues}
- value={formatMeasure(fixedCount, MetricType.ShortInteger)}
- disabled={component.needIssueSync}
- url={fixedUrl}
- icon={fixedCount && fixedCount !== '0' && <TrendDownCircleIcon />}
- footer={
- <TextSubdued className="sw-body-xs">
- {intl.formatMessage({ id: 'overview.pull_request.fixed_issues.help' })}
- </TextSubdued>
- }
- />
- </LightGreyCard>
- );
-}
-
-const StyledCardSeparator = styled.div`
- width: 1px;
- background-color: ${themeColor('projectCardBorder')};
-`;
diff --git a/server/sonar-web/src/main/js/apps/overview/pullRequests/MeasuresCardPanel.tsx b/server/sonar-web/src/main/js/apps/overview/pullRequests/MeasuresCardPanel.tsx
index fc313e846e2..14bb087b4e5 100644
--- a/server/sonar-web/src/main/js/apps/overview/pullRequests/MeasuresCardPanel.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/pullRequests/MeasuresCardPanel.tsx
@@ -18,22 +18,51 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import classNames from 'classnames';
+import {
+ Card,
+ HelperHintIcon,
+ LightLabel,
+ SnoozeCircleIcon,
+ TextError,
+ TextSubdued,
+ TrendDownCircleIcon,
+ TrendUpCircleIcon,
+} from 'design-system/lib';
import * as React from 'react';
-import { getComponentSecurityHotspotsUrl } from '~sonar-aligned/helpers/urls';
-import { MetricKey } from '~sonar-aligned/types/metrics';
+import { useIntl } from 'react-intl';
+import {
+ getComponentIssuesUrl,
+ getComponentSecurityHotspotsUrl,
+} from '~sonar-aligned/helpers/urls';
+import { MetricKey, MetricType } from '~sonar-aligned/types/metrics';
+import Tooltip from '../../../components/controls/Tooltip';
import { getLeakValue } from '../../../components/measure/utils';
+import { DEFAULT_ISSUES_QUERY } from '../../../components/shared/utils';
import { findMeasure } from '../../../helpers/measures';
import { getComponentDrilldownUrl } from '../../../helpers/urls';
+import { getBranchLikeQuery } from '../../../sonar-aligned/helpers/branch-like';
+import { formatMeasure } from '../../../sonar-aligned/helpers/measures';
import { PullRequest } from '../../../types/branch-like';
import { QualityGateStatusConditionEnhanced } from '../../../types/quality-gates';
import { Component, MeasureEnhanced } from '../../../types/types';
+import {
+ GridContainer,
+ StyleMeasuresCard,
+ StyleMeasuresCardRightBorder,
+ StyledConditionsCard,
+} from '../branches/BranchSummaryStyles';
+import FailedConditions from '../branches/FailedConditions';
+import { IssueMeasuresCardInner } from '../components/IssueMeasuresCardInner';
import MeasuresCardNumber from '../components/MeasuresCardNumber';
import MeasuresCardPercent from '../components/MeasuresCardPercent';
-import { MeasurementType, getMeasurementMetricKey } from '../utils';
-import IssueMeasuresCard from './IssueMeasuresCard';
+import {
+ MeasurementType,
+ Status,
+ getConditionRequiredLabel,
+ getMeasurementMetricKey,
+} from '../utils';
interface Props {
- className?: string;
component: Component;
conditions: QualityGateStatusConditionEnhanced[];
measures: MeasureEnhanced[];
@@ -41,23 +70,144 @@ interface Props {
}
export default function MeasuresCardPanel(props: React.PropsWithChildren<Props>) {
- const { pullRequest, component, measures, conditions, className } = props;
+ const { pullRequest, component, measures, conditions } = props;
const newSecurityHotspots = getLeakValue(
findMeasure(measures, MetricKey.new_security_hotspots),
) as string;
- return (
- <>
- <IssueMeasuresCard
- conditions={conditions}
- measures={measures}
- component={component}
- pullRequest={pullRequest}
- />
+ const intl = useIntl();
+
+ const issuesCount = getLeakValue(findMeasure(measures, MetricKey.new_violations));
+ const issuesCondition = conditions.find((c) => c.metric === MetricKey.new_violations);
+ const isIssuesConditionFailed = issuesCondition?.level === Status.ERROR;
+ const fixedCount = findMeasure(measures, MetricKey.pull_request_fixed_issues)?.value;
+ const acceptedCount = getLeakValue(findMeasure(measures, MetricKey.new_accepted_issues));
+
+ const issuesUrl = getComponentIssuesUrl(component.key, {
+ ...getBranchLikeQuery(pullRequest),
+ ...DEFAULT_ISSUES_QUERY,
+ });
+ const fixedUrl = getComponentIssuesUrl(component.key, {
+ fixedInPullRequest: pullRequest.key,
+ });
+ const acceptedUrl = getComponentIssuesUrl(component.key, {
+ ...getBranchLikeQuery(pullRequest),
+ ...DEFAULT_ISSUES_QUERY,
+ issueStatuses: 'ACCEPTED',
+ });
- <div className={classNames('sw-w-full sw-flex sw-flex-row sw-gap-4 sw-mt-4', className)}>
- <div className="sw-flex-1 sw-flex sw-flex-col sw-gap-4">
+ const totalFailedConditions = conditions.filter((condition) => condition.level === Status.ERROR);
+
+ return (
+ <Card>
+ <GridContainer
+ className={classNames('sw-relative sw-overflow-hidden sw-mt-8 js-summary', {
+ 'sw-grid-cols-3': totalFailedConditions.length === 0,
+ 'sw-grid-cols-4': totalFailedConditions.length > 0,
+ })}
+ >
+ {totalFailedConditions.length > 0 && (
+ <StyledConditionsCard className="sw-row-span-3">
+ <FailedConditions
+ branchLike={pullRequest}
+ failedConditions={totalFailedConditions}
+ isNewCode
+ component={component}
+ />
+ </StyledConditionsCard>
+ )}
+ <StyleMeasuresCard>
+ <IssueMeasuresCardInner
+ header={intl.formatMessage({ id: 'overview.new_issues' })}
+ data-testid={`overview__measures-${MetricKey.new_violations}`}
+ data-guiding-id={
+ isIssuesConditionFailed ? 'overviewZeroNewIssuesSimplification' : undefined
+ }
+ metric={MetricKey.new_violations}
+ value={formatMeasure(issuesCount, MetricType.ShortInteger)}
+ url={issuesUrl}
+ failed={isIssuesConditionFailed}
+ icon={isIssuesConditionFailed && <TrendUpCircleIcon />}
+ footer={
+ issuesCondition &&
+ (isIssuesConditionFailed ? (
+ <TextError
+ className="sw-font-regular sw-body-xs sw-inline"
+ text={getConditionRequiredLabel(issuesCondition, intl, true)}
+ />
+ ) : (
+ <LightLabel className="sw-body-xs">
+ {getConditionRequiredLabel(issuesCondition, intl)}
+ </LightLabel>
+ ))
+ }
+ />
+ </StyleMeasuresCard>
+ <StyleMeasuresCard>
+ <IssueMeasuresCardInner
+ header={intl.formatMessage({ id: 'overview.accepted_issues' })}
+ data-testid={`overview__measures-${MetricKey.new_accepted_issues}`}
+ metric={MetricKey.new_accepted_issues}
+ value={formatMeasure(acceptedCount, MetricType.ShortInteger)}
+ disabled={component.needIssueSync}
+ url={acceptedUrl}
+ icon={
+ acceptedCount && (
+ <SnoozeCircleIcon
+ color={
+ acceptedCount === '0' ? 'overviewCardDefaultIcon' : 'overviewCardWarningIcon'
+ }
+ />
+ )
+ }
+ footer={
+ <TextSubdued className="sw-body-xs">
+ {intl.formatMessage({ id: 'overview.accepted_issues.help' })}
+ </TextSubdued>
+ }
+ />
+ </StyleMeasuresCard>
+ <StyleMeasuresCard>
+ <IssueMeasuresCardInner
+ header={
+ <>
+ {intl.formatMessage({ id: 'overview.pull_request.fixed_issues' })}
+ <Tooltip
+ content={
+ <div className="sw-flex sw-flex-col sw-gap-4">
+ <span>
+ {intl.formatMessage({
+ id: 'overview.pull_request.fixed_issues.disclaimer',
+ })}
+ </span>
+ <span>
+ {intl.formatMessage({
+ id: 'overview.pull_request.fixed_issues.disclaimer.2',
+ })}
+ </span>
+ </div>
+ }
+ side="top"
+ >
+ <HelperHintIcon raised />
+ </Tooltip>
+ </>
+ }
+ data-testid={`overview__measures-${MetricKey.pull_request_fixed_issues}`}
+ metric={MetricKey.pull_request_fixed_issues}
+ value={formatMeasure(fixedCount, MetricType.ShortInteger)}
+ disabled={component.needIssueSync}
+ url={fixedUrl}
+ icon={fixedCount && fixedCount !== '0' && <TrendDownCircleIcon />}
+ footer={
+ <TextSubdued className="sw-body-xs">
+ {intl.formatMessage({ id: 'overview.pull_request.fixed_issues.help' })}
+ </TextSubdued>
+ }
+ />
+ </StyleMeasuresCard>
+ <StyleMeasuresCardRightBorder>
<MeasuresCardPercent
componentKey={component.key}
branchLike={pullRequest}
@@ -77,23 +227,8 @@ export default function MeasuresCardPanel(props: React.PropsWithChildren<Props>)
showRequired
useDiffMetric
/>
-
- <MeasuresCardNumber
- label={
- newSecurityHotspots === '1'
- ? 'issue.type.SECURITY_HOTSPOT'
- : 'issue.type.SECURITY_HOTSPOT.plural'
- }
- url={getComponentSecurityHotspotsUrl(component.key, pullRequest)}
- value={newSecurityHotspots}
- metric={MetricKey.new_security_hotspots}
- conditions={conditions}
- conditionMetric={MetricKey.new_security_hotspots_reviewed}
- showRequired
- />
- </div>
-
- <div className="sw-flex-1 sw-flex sw-flex-col sw-gap-4">
+ </StyleMeasuresCardRightBorder>
+ <StyleMeasuresCardRightBorder>
<MeasuresCardPercent
componentKey={component.key}
branchLike={pullRequest}
@@ -113,8 +248,23 @@ export default function MeasuresCardPanel(props: React.PropsWithChildren<Props>)
useDiffMetric
showRequired
/>
+ </StyleMeasuresCardRightBorder>
+ <div>
+ <MeasuresCardNumber
+ label={
+ newSecurityHotspots === '1'
+ ? 'issue.type.SECURITY_HOTSPOT'
+ : 'issue.type.SECURITY_HOTSPOT.plural'
+ }
+ url={getComponentSecurityHotspotsUrl(component.key, pullRequest)}
+ value={newSecurityHotspots}
+ metric={MetricKey.new_security_hotspots}
+ conditions={conditions}
+ conditionMetric={MetricKey.new_security_hotspots_reviewed}
+ showRequired
+ />
</div>
- </div>
- </>
+ </GridContainer>
+ </Card>
);
}
diff --git a/server/sonar-web/src/main/js/apps/overview/pullRequests/PullRequestOverview.tsx b/server/sonar-web/src/main/js/apps/overview/pullRequests/PullRequestOverview.tsx
index dc1954991d0..e5778e1bd48 100644
--- a/server/sonar-web/src/main/js/apps/overview/pullRequests/PullRequestOverview.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/pullRequests/PullRequestOverview.tsx
@@ -98,8 +98,6 @@ export default function PullRequestOverview(props: Readonly<Readonly<Props>>) {
<PullRequestMetaTopBar pullRequest={pullRequest} measures={measures} />
<BasicSeparator className="sw-my-4" />
- <AnalysisStatus className="sw-mb-4" component={component} />
-
{ignoredConditions && <IgnoredConditionWarning />}
<div className="sw-flex sw-justify-between sw-items-start sw-my-6">
@@ -107,8 +105,9 @@ export default function PullRequestOverview(props: Readonly<Readonly<Props>>) {
<LastAnalysisLabel analysisDate={pullRequest.analysisDate} />
</div>
+ <AnalysisStatus className="sw-mb-4" component={component} />
+
<MeasuresCardPanel
- className="sw-flex-1"
pullRequest={pullRequest}
component={component}
conditions={enhancedConditions}