* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { BasicSeparator, LargeCenteredLayout, PageContentFontWrapper } from 'design-system';
+import { BasicSeparator, Card, LargeCenteredLayout, PageContentFontWrapper } from 'design-system';
import * as React from 'react';
import A11ySkipTarget from '../../../components/a11y/A11ySkipTarget';
import { useLocation } from '../../../components/hoc/withRouter';
import { QualityGateStatus } from '../../../types/quality-gates';
import { Component, MeasureEnhanced, Metric, Period, QualityGate } from '../../../types/types';
import { AnalysisStatus } from '../components/AnalysisStatus';
+import SonarLintPromotion from '../components/SonarLintPromotion';
import { MeasuresTabs } from '../utils';
import AcceptedIssuesPanel from './AcceptedIssuesPanel';
import ActivityPanel from './ActivityPanel';
</>
)}
<AnalysisStatus className="sw-mt-6" component={component} />
- <div className="sw-flex">
- <div className="sw-w-1/3 sw-mr-12 sw-pt-6">
- <QualityGatePanel
- component={component}
- loading={loadingStatus}
- qgStatuses={qgStatuses}
- qualityGate={qualityGate}
+ <div className="sw-flex sw-mt-6">
+ <div className="sw-w-1/4 sw-mr-3">
+ <Card className=" sw-h-max">
+ <QualityGatePanel
+ component={component}
+ loading={loadingStatus}
+ qgStatuses={qgStatuses}
+ qualityGate={qualityGate}
+ />
+ </Card>
+ <SonarLintPromotion
+ qgConditions={qgStatuses?.flatMap((qg) => qg.failedConditions)}
/>
</div>
- <div className="sw-flex-1">
- <div className="sw-flex sw-flex-col sw-pt-6">
+ <Card className="sw-flex-1 sw-pt-4">
+ <div className="sw-flex sw-flex-col">
<TabsPanel
analyses={analyses}
appLeak={appLeak}
component={component}
loading={loadingStatus}
period={period}
+ branch={branch}
qgStatuses={qgStatuses}
isNewCode={isNewCodeTab}
onTabSelect={selectTab}
onGraphChange={onGraphChange}
/>
</div>
- </div>
+ </Card>
</div>
</div>
)}
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { BasicSeparator, Card, Spinner } from 'design-system';
-import { flatMap } from 'lodash';
import * as React from 'react';
import { ComponentQualifier, isApplication } from '../../../types/component';
import { QualityGateStatus } from '../../../types/quality-gates';
import QualityGateStatusHeader from '../components/QualityGateStatusHeader';
import QualityGateStatusPassedView from '../components/QualityGateStatusPassedView';
import { QualityGateStatusTitle } from '../components/QualityGateStatusTitle';
-import SonarLintPromotion from '../components/SonarLintPromotion';
import ApplicationNonCaycProjectWarning from './ApplicationNonCaycProjectWarning';
import CleanAsYouCodeWarning from './CleanAsYouCodeWarning';
import QualityGatePanelSection from './QualityGatePanelSection';
return (
<div data-test="overview__quality-gate-panel">
<QualityGateStatusTitle />
- <Card>
- <div>
- {loading ? (
- <div className="sw-p-6">
- <Spinner loading={loading} />
+ <div className="sw-pt-5">
+ <Spinner loading={loading}>
+ <QualityGateStatusHeader
+ status={overallLevel}
+ failedConditionCount={overallFailedConditionsCount}
+ />
+ {success && <QualityGateStatusPassedView />}
+
+ {showIgnoredConditionWarning && <IgnoredConditionWarning />}
+
+ {!success && <BasicSeparator />}
+
+ {overallFailedConditionsCount > 0 && (
+ <div data-test="overview__quality-gate-conditions">
+ {failedQgStatuses.map((qgStatus, qgStatusIdx) => (
+ <QualityGatePanelSection
+ isApplication={isApp}
+ isLastStatus={qgStatusIdx === failedQgStatuses.length - 1}
+ key={qgStatus.key}
+ qgStatus={qgStatus}
+ qualityGate={qualityGate}
+ />
+ ))}
</div>
- ) : (
- <>
- <QualityGateStatusHeader
- status={overallLevel}
- failedConditionCount={overallFailedConditionsCount}
- />
- {success && <QualityGateStatusPassedView />}
-
- {showIgnoredConditionWarning && <IgnoredConditionWarning />}
-
- {!success && <BasicSeparator />}
-
- {overallFailedConditionsCount > 0 && (
- <div data-test="overview__quality-gate-conditions">
- {failedQgStatuses.map((qgStatus, qgStatusIdx) => (
- <QualityGatePanelSection
- isApplication={isApp}
- isLastStatus={qgStatusIdx === failedQgStatuses.length - 1}
- key={qgStatus.key}
- qgStatus={qgStatus}
- qualityGate={qualityGate}
- />
- ))}
- </div>
- )}
- </>
)}
- </div>
- </Card>
+ </Spinner>
+ </div>
{nonCaycProjectsInApp.length > 0 && (
<ApplicationNonCaycProjectWarning projects={nonCaycProjectsInApp} />
<CleanAsYouCodeWarning component={component} />
</Card>
)}
-
- <SonarLintPromotion
- qgConditions={flatMap(qgStatuses, (qgStatus) => qgStatus.failedConditions)}
- />
</div>
);
}
import { isBefore, sub } from 'date-fns';
import {
ButtonLink,
+ CardSeparator,
FlagMessage,
LightLabel,
PageTitle,
ToggleButton,
} from 'design-system';
import * as React from 'react';
-import { FormattedMessage } from 'react-intl';
+import { FormattedMessage, useIntl } from 'react-intl';
import DocLink from '../../../components/common/DocLink';
-import { translate, translateWithParameters } from '../../../helpers/l10n';
+import { translate } from '../../../helpers/l10n';
import { isDiffMetric } from '../../../helpers/measures';
import { ApplicationPeriod } from '../../../types/application';
+import { Branch } from '../../../types/branch-like';
import { ComponentQualifier } from '../../../types/component';
import { Analysis, ProjectAnalysisEventCategory } from '../../../types/project-activity';
import { QualityGateStatus } from '../../../types/quality-gates';
import { Component, Period } from '../../../types/types';
+import LastAnalysisLabel from '../components/LastAnalysisLabel';
import { MeasuresTabs } from '../utils';
import { MAX_ANALYSES_NB } from './ActivityPanel';
import { LeakPeriodInfo } from './LeakPeriodInfo';
component: Component;
loading?: boolean;
period?: Period;
+ branch?: Branch;
qgStatuses?: QualityGateStatus[];
isNewCode: boolean;
onTabSelect: (tab: MeasuresTabs) => void;
period,
qgStatuses = [],
isNewCode,
+ branch,
children,
} = props;
-
+ const intl = useIntl();
const isApp = component.qualifier === ComponentQualifier.Application;
const leakPeriod = isApp ? appLeak : period;
return (
<div data-test="overview__measures-panel">
- <div className="sw-flex sw-mb-4">
+ <div className="sw-flex sw-justify-between sw-items-center sw-mb-4">
<PageTitle as="h2" text={translate('overview.measures')} />
+ <LastAnalysisLabel analysisDate={branch?.analysisDate} />
</div>
+ <CardSeparator className="sw--mx-6 sw-mb-3" />
{loading ? (
<div>
/>
{failingConditions > 0 && (
<LightLabel className="sw-body-sm-highlight sw-ml-8">
- {failingConditions === 1
- ? translate('overview.1_condition_failed')
- : translateWithParameters('overview.X_conditions_failed', failingConditions)}
+ {intl.formatMessage(
+ { id: 'overview.X_conditions_failed' },
+ { conditions: failingConditions },
+ )}
</LightLabel>
)}
</div>
renderBranchOverview();
expect(await screen.findByText('metric.level.ERROR')).toBeInTheDocument();
- expect(screen.getAllByText('overview.X_conditions_failed.2')).toHaveLength(2);
+ expect(screen.getAllByText(/overview.X_conditions_failed/)).toHaveLength(2);
});
it('should correctly show a project as empty', async () => {
--- /dev/null
+/*
+ * 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 React from 'react';
+import { useIntl } from 'react-intl';
+import DateFromNow from '../../../components/intl/DateFromNow';
+
+interface Props {
+ analysisDate?: string;
+}
+
+export default function LastAnalysisLabel({ analysisDate }: Readonly<Props>) {
+ const intl = useIntl();
+
+ return analysisDate ? (
+ <span>
+ {intl.formatMessage(
+ {
+ id: 'overview.last_analysis_x',
+ },
+ {
+ date: <DateFromNow className="sw-body-sm-highlight" date={analysisDate} />,
+ },
+ )}
+ </span>
+ ) : null;
+}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { QualityGateIndicator, TextError, TextMuted } from 'design-system';
+import { QualityGateIndicator, TextError } from 'design-system';
import React from 'react';
-import { translate, translateWithParameters } from '../../../helpers/l10n';
+import { useIntl } from 'react-intl';
+import { translate } from '../../../helpers/l10n';
import { Status } from '../../../types/types';
interface Props {
export default function QualityGateStatusHeader(props: Props) {
const { status, failedConditionCount } = props;
+ const intl = useIntl();
return (
<div className="sw-flex sw-items-center sw-mb-4">
<QualityGateIndicator status={status} className="sw-mr-2" size="xl" />
<div className="sw-flex sw-flex-col">
- <div>
- <TextMuted text={translate('overview.quality_gate')} />
- </div>
- <div>
- <span className="sw-heading-lg">{translate('metric.level', status)}</span>
- </div>
- </div>
- <div className="sw-flex sw-flex-1 sw-justify-end">
+ <span className="sw-heading-lg">{translate('metric.level', status)}</span>
{failedConditionCount > 0 && (
<TextError
- text={
- failedConditionCount === 1
- ? translate('overview.1_condition_failed')
- : translateWithParameters('overview.X_conditions_failed', failedConditionCount)
- }
+ className="sw-font-regular"
+ text={intl.formatMessage(
+ { id: 'overview.X_conditions_failed' },
+ {
+ conditions: <strong>{failedConditionCount}</strong>,
+ },
+ )}
/>
)}
</div>
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { HelperHintIcon, PageTitle } from 'design-system';
+import { CardSeparator, HelperHintIcon, PageTitle } from 'design-system';
import React from 'react';
import HelpTooltip from '../../../components/controls/HelpTooltip';
import { translate } from '../../../helpers/l10n';
export function QualityGateStatusTitle() {
return (
- <div className="sw-flex sw-items-center sw-mb-4">
- <div className="sw-flex sw-items-center">
- <PageTitle as="h2" text={translate('overview.quality_gate.status')} />
- <HelpTooltip
- className="sw-ml-2"
- overlay={<div className="sw-my-4">{translate('overview.quality_gate.help')}</div>}
- >
- <HelperHintIcon aria-label="help-tooltip" />
- </HelpTooltip>
+ <>
+ <div className="sw-flex sw-items-center sw-mb-4 sw--mt-2">
+ <div className="sw-flex sw-items-center">
+ <PageTitle as="h2" text={translate('overview.quality_gate.status')} />
+ <HelpTooltip
+ className="sw-ml-2"
+ overlay={<div className="sw-my-4">{translate('overview.quality_gate.help')}</div>}
+ >
+ <HelperHintIcon aria-label="help-tooltip" />
+ </HelpTooltip>
+ </div>
</div>
- </div>
+ <CardSeparator className="sw--mx-6" />
+ </>
);
}
import React from 'react';
import { useIntl } from 'react-intl';
import CurrentBranchLikeMergeInformation from '../../../app/components/nav/component/branch-like/CurrentBranchLikeMergeInformation';
-import DateFromNow from '../../../components/intl/DateFromNow';
import { getLeakValue } from '../../../components/measure/utils';
import { findMeasure, formatMeasure } from '../../../helpers/measures';
import { PullRequest } from '../../../types/branch-like';
import { MetricKey, MetricType } from '../../../types/metrics';
import { MeasureEnhanced } from '../../../types/types';
+import LastAnalysisLabel from '../components/LastAnalysisLabel';
interface Props {
pullRequest: PullRequest;
{pullRequest.analysisDate && (
<>
<SeparatorCircleIcon />
- {intl.formatMessage(
- {
- id: 'overview.last_analysis_x',
- },
- {
- date: (
- <strong className="sw-body-sm-highlight">
- <DateFromNow date={pullRequest.analysisDate} />
- </strong>
- ),
- },
- )}
+ <LastAnalysisLabel analysisDate={pullRequest.analysisDate} />
</>
)}
</div>
# OVERVIEW
#
#------------------------------------------------------------------------------
-overview.1_condition_failed=1 failed condition
-overview.X_conditions_failed={0} failed conditions
+overview.X_conditions_failed={conditions} {conditions, plural, one {failed condition} other {failed conditions}}
overview.failed_condition.x_rating_required={rating} is {value}. Required {threshold}
overview.failed_condition.x_required={metric}. Required {threshold}
overview.fix_failed_conditions_with_sonarlint=Fix issues before they fail your Quality Gate with {link} in your IDE. Power up with connected mode!