`};
`;
+export const NakedLink = styled(BaseLink)`
+ border-bottom: none;
+ font-weight: 600;
+ color: ${themeColor('linkNaked')};
+`;
+
export const DrilldownLink = styled(StyledBaseLink)`
${tw`sw-heading-lg`}
${tw`sw-tracking-tight`}
import { getProp, themeColor, themeContrast } from '../helpers/theme';
import { MetricsLabel } from '../types/measures';
-type sizeType = 'xs' | 'sm' | 'md' | 'xl';
+type sizeType = keyof typeof SIZE_MAPPING;
interface Props extends React.AriaAttributes {
className?: string;
label: string;
xs: '1rem',
sm: '1.5rem',
md: '2rem',
+ lg: '2.8rem',
xl: '4rem',
};
color: ${({ rating }) => themeContrast(`rating.${rating}`)};
font-size: ${({ size }) => getFontSize(size)};
background-color: ${({ rating }) => themeColor(`rating.${rating}`)};
+ user-select: none;
display: inline-flex;
align-items: center;
background-color: ${themeColor('popupBorder')};
`;
+export const CardSeparator = styled(BasicSeparator)`
+ background-color: ${themeColor('projectCardBorder')};
+`;
+
export const GreySeparator = styled(BasicSeparator)`
background-color: ${themeColor('subnavigationBorder')};
`;
// links
linkDefault: primary.default,
+ linkNaked: COLORS.blueGrey[700],
linkActive: COLORS.indigo[600],
linkDiscreet: 'currentColor',
linkTooltipDefault: COLORS.indigo[200],
overviewCardErrorIcon: COLORS.red[100],
overviewCardSuccessIcon: COLORS.green[200],
+ // overview software impact breakdown
+ overviewSoftwareImpactSeverityNeutral: COLORS.blueGrey[50],
+ overviewSoftwareImpactSeverityHigh: COLORS.red[100],
+ overviewSoftwareImpactSeverityMedium: COLORS.yellow[100],
+ overviewSoftwareImpactSeverityLow: COLORS.blue[100],
+
// graph - chart
graphPointCircleColor: COLORS.white,
'graphLineColor.0': COLORS.blue[500],
--- /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 * as React from 'react';
+import { SoftwareQuality } from '../../../types/clean-code-taxonomy';
+import { MetricKey } from '../../../types/metrics';
+import { Component, MeasureEnhanced } from '../../../types/types';
+import SoftwareImpactMeasureCard from './SoftwareImpactMeasureCard';
+
+export interface BranchOverallCodePanelProps {
+ component: Component;
+ measures: MeasureEnhanced[];
+}
+
+export default function BranchOverallCodePanel(props: Readonly<BranchOverallCodePanelProps>) {
+ const { component, measures } = props;
+
+ return (
+ <div className="sw-flex sw-gap-4">
+ <SoftwareImpactMeasureCard
+ component={component}
+ softwareQuality={SoftwareQuality.Security}
+ ratingMetricKey={MetricKey.security_rating}
+ measures={measures}
+ />
+ <SoftwareImpactMeasureCard
+ component={component}
+ softwareQuality={SoftwareQuality.Reliability}
+ ratingMetricKey={MetricKey.reliability_rating}
+ measures={measures}
+ />
+ <SoftwareImpactMeasureCard
+ component={component}
+ softwareQuality={SoftwareQuality.Maintainability}
+ ratingMetricKey={MetricKey.sqale_rating}
+ measures={measures}
+ />
+ </div>
+ );
+}
import AcceptedIssuesPanel from './AcceptedIssuesPanel';
import ActivityPanel from './ActivityPanel';
import BranchMetaTopBar from './BranchMetaTopBar';
+import BranchOverallCodePanel from './BranchOverallCodePanel';
import FirstAnalysisNextStepsNotif from './FirstAnalysisNextStepsNotif';
import { MeasuresPanel } from './MeasuresPanel';
import MeasuresPanelNoNewCode from './MeasuresPanelNoNewCode';
{!isNewCodeTab && (
<>
+ {!isNewCodeTab && (
+ <BranchOverallCodePanel component={component} measures={measures} />
+ )}
+
<MeasuresPanel
branch={branch}
component={component}
--- /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 styled from '@emotion/styled';
+import classNames from 'classnames';
+import { DiscreetLinkBox, Tooltip, themeColor } from 'design-system';
+import * as React from 'react';
+import { useIntl } from 'react-intl';
+import { DEFAULT_ISSUES_QUERY } from '../../../components/shared/utils';
+import { formatMeasure } from '../../../helpers/measures';
+import { getComponentIssuesUrl } from '../../../helpers/urls';
+import { SoftwareImpactSeverity, SoftwareQuality } from '../../../types/clean-code-taxonomy';
+import { MetricType } from '../../../types/metrics';
+import { Component } from '../../../types/types';
+
+export interface SoftwareImpactMeasureBreakdownCardProps {
+ softwareQuality: SoftwareQuality;
+ component: Component;
+ value: string;
+ severity: SoftwareImpactSeverity;
+ active?: boolean;
+}
+
+export function SoftwareImpactMeasureBreakdownCard(
+ props: Readonly<SoftwareImpactMeasureBreakdownCardProps>,
+) {
+ const { softwareQuality, component, value, severity, active } = props;
+
+ const intl = useIntl();
+
+ const url = getComponentIssuesUrl(component.key, {
+ ...DEFAULT_ISSUES_QUERY,
+ impactSoftwareQualities: softwareQuality,
+ impactSeverities: severity,
+ });
+
+ return (
+ <Tooltip
+ overlay={intl.formatMessage({
+ id: `overview.measures.software_impact.severity.${severity}.tooltip`,
+ })}
+ >
+ <StyledBreakdownCard
+ aria-label={intl.formatMessage(
+ {
+ id: 'overview.measures.software_impact.severity.see_x_open_issues',
+ },
+ {
+ count: formatMeasure(value, MetricType.ShortInteger),
+ softwareQuality: intl.formatMessage({
+ id: `software_quality.${softwareQuality}`,
+ }),
+ severity: intl.formatMessage({
+ id: `overview.measures.software_impact.severity.${severity}.tooltip`,
+ }),
+ },
+ )}
+ to={url}
+ className={classNames(
+ 'sw-w-1/3 sw-p-2 sw-rounded-1 sw-text-xs sw-font-semibold sw-select-none sw-flex sw-gap-1 sw-justify-center sw-items-center',
+ severity,
+ {
+ active,
+ },
+ )}
+ >
+ <span>{formatMeasure(value, MetricType.ShortInteger)}</span>
+ <span>
+ {intl.formatMessage({
+ id: `overview.measures.software_impact.severity.${severity}`,
+ })}
+ </span>
+ </StyledBreakdownCard>
+ </Tooltip>
+ );
+}
+
+const StyledBreakdownCard = styled(DiscreetLinkBox)`
+ background-color: ${themeColor('overviewSoftwareImpactSeverityNeutral')};
+
+ &.active.HIGH {
+ background-color: ${themeColor('overviewSoftwareImpactSeverityHigh')};
+ }
+ &.active.MEDIUM {
+ background-color: ${themeColor('overviewSoftwareImpactSeverityMedium')};
+ }
+ &.active.LOW {
+ background-color: ${themeColor('overviewSoftwareImpactSeverityLow')};
+ }
+`;
+
+export default SoftwareImpactMeasureBreakdownCard;
--- /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 styled from '@emotion/styled';
+import { Card, CardSeparator, NakedLink, TextBold, TextSubdued, themeBorder } from 'design-system';
+import * as React from 'react';
+import { useIntl } from 'react-intl';
+import { DEFAULT_ISSUES_QUERY } from '../../../components/shared/utils';
+import { formatMeasure, formatRating } from '../../../helpers/measures';
+import { getComponentIssuesUrl } from '../../../helpers/urls';
+import {
+ SoftwareImpactMeasureData,
+ SoftwareImpactSeverity,
+ SoftwareQuality,
+} from '../../../types/clean-code-taxonomy';
+import { MetricKey, MetricType } from '../../../types/metrics';
+import { Component, MeasureEnhanced } from '../../../types/types';
+import { getSoftwareImpactSeverityValue, softwareQualityToMeasure } from '../utils';
+import SoftwareImpactMeasureBreakdownCard from './SoftwareImpactMeasureBreakdownCard';
+import SoftwareImpactMeasureRating from './SoftwareImpactMeasureRating';
+
+export interface SoftwareImpactBreakdownCardProps {
+ component: Component;
+ softwareQuality: SoftwareQuality;
+ ratingMetricKey: MetricKey;
+ measures: MeasureEnhanced[];
+}
+
+export function SoftwareImpactMeasureCard(props: Readonly<SoftwareImpactBreakdownCardProps>) {
+ const { component, softwareQuality, ratingMetricKey, measures } = props;
+
+ const intl = useIntl();
+
+ // Find measure for this software quality
+ const metricKey = softwareQualityToMeasure(softwareQuality);
+ const measureRaw = measures.find((m) => m.metric.key === metricKey);
+ const measure = JSON.parse(measureRaw?.value ?? 'null') as SoftwareImpactMeasureData | null;
+
+ // Hide this card if there is no measure
+ if (!measure) {
+ return null;
+ }
+
+ // Find rating measure
+ const ratingMeasure = measures.find((m) => m.metric.key === ratingMetricKey);
+ const ratingLabel = ratingMeasure?.value ? formatRating(ratingMeasure.value) : undefined;
+
+ const totalLinkHref = getComponentIssuesUrl(component.key, {
+ ...DEFAULT_ISSUES_QUERY,
+ impactSoftwareQualities: softwareQuality,
+ });
+
+ // We highlight the highest severity breakdown card with non-zero count if the rating is not A
+ const issuesBySeverity = {
+ [SoftwareImpactSeverity.High]: measure.high,
+ [SoftwareImpactSeverity.Medium]: measure.medium,
+ [SoftwareImpactSeverity.Low]: measure.low,
+ };
+ const shouldHighlightSeverity = measure && (!ratingLabel || ratingLabel !== 'A');
+ const highlightedSeverity = shouldHighlightSeverity
+ ? Object.entries(issuesBySeverity).find(([_, issuesCount]) => issuesCount > 0)?.[0]
+ : null;
+
+ return (
+ <StyledCard className="sw-w-1/3 sw-rounded-2 sw-p-4 sw-flex-col">
+ <TextBold name={intl.formatMessage({ id: `software_quality.${softwareQuality}` })} />
+ <CardSeparator className="sw--mx-4" />
+ <div className="sw-flex sw-flex-col sw-gap-3">
+ <div className="sw-flex sw-gap-1 sw-items-end">
+ <NakedLink
+ aria-label={intl.formatMessage(
+ {
+ id: `overview.measures.software_impact.see_list_of_x_open_issues`,
+ },
+ {
+ count: measure.total,
+ softwareQuality: intl.formatMessage({
+ id: `software_quality.${softwareQuality}`,
+ }),
+ },
+ )}
+ className="sw-text-xl"
+ to={totalLinkHref}
+ >
+ {formatMeasure(measure.total, MetricType.ShortInteger)}
+ </NakedLink>
+ <TextSubdued className="sw-body-sm sw-mb-2">
+ {intl.formatMessage({ id: 'overview.measures.software_impact.total_open_issues' })}
+ </TextSubdued>
+ <div className="sw-flex-grow sw-flex sw-justify-end">
+ <SoftwareImpactMeasureRating
+ softwareQuality={softwareQuality}
+ value={ratingMeasure?.value}
+ />
+ </div>
+ </div>
+ <div className="sw-flex sw-gap-2">
+ {[
+ SoftwareImpactSeverity.High,
+ SoftwareImpactSeverity.Medium,
+ SoftwareImpactSeverity.Low,
+ ].map((severity) => (
+ <SoftwareImpactMeasureBreakdownCard
+ key={severity}
+ component={component}
+ softwareQuality={softwareQuality}
+ value={getSoftwareImpactSeverityValue(severity, measure)}
+ severity={severity}
+ active={highlightedSeverity === severity}
+ />
+ ))}
+ </div>
+ </div>
+ </StyledCard>
+ );
+}
+
+const StyledCard = styled(Card)`
+ border: ${themeBorder('default')};
+`;
+
+export default SoftwareImpactMeasureCard;
--- /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 styled from '@emotion/styled';
+import { BasicSeparator, MetricsRatingBadge, Tooltip, themeColor } from 'design-system';
+import * as React from 'react';
+import { useIntl } from 'react-intl';
+import { formatRating } from '../../../helpers/measures';
+import { SoftwareImpactSeverity, SoftwareQuality } from '../../../types/clean-code-taxonomy';
+
+export interface SoftwareImpactMeasureRatingProps {
+ softwareQuality: SoftwareQuality;
+ value?: string;
+}
+
+export function SoftwareImpactMeasureRating(props: Readonly<SoftwareImpactMeasureRatingProps>) {
+ const { softwareQuality, value } = props;
+
+ const intl = useIntl();
+
+ const rating = formatRating(value);
+
+ function ratingToWorseSeverity(rating: string): SoftwareImpactSeverity {
+ switch (rating) {
+ case 'B':
+ return SoftwareImpactSeverity.Low;
+ case 'C':
+ return SoftwareImpactSeverity.Medium;
+ case 'D':
+ case 'E':
+ return SoftwareImpactSeverity.High;
+ default:
+ return SoftwareImpactSeverity.Low;
+ }
+ }
+
+ function getTooltipContent() {
+ if (!rating || rating === 'A') {
+ return null;
+ }
+
+ const softwareQualityLabel = intl.formatMessage({
+ id: `software_quality.${softwareQuality}`,
+ });
+ const severityLabel = intl.formatMessage({
+ id: `overview.measures.software_impact.severity.${ratingToWorseSeverity(
+ rating,
+ )}.improve_tooltip`,
+ });
+
+ return (
+ <div className="sw-flex sw-flex-col sw-gap-1">
+ <span className="sw-font-semibold">
+ {intl.formatMessage({
+ id: 'overview.measures.software_impact.improve_rating_tooltip.title',
+ })}
+ </span>
+
+ <span>
+ {intl.formatMessage(
+ {
+ id: 'overview.measures.software_impact.improve_rating_tooltip.content.1',
+ },
+ {
+ softwareQuality: softwareQualityLabel,
+ ratingLabel: rating,
+ severity: severityLabel,
+ },
+ )}
+ </span>
+
+ <span className="sw-mt-4">
+ {intl.formatMessage({
+ id: 'overview.measures.software_impact.improve_rating_tooltip.content.2',
+ })}
+ </span>
+ </div>
+ );
+ }
+
+ return (
+ <Tooltip overlay={getTooltipContent()}>
+ <MetricsRatingBadge
+ size="lg"
+ className="sw-text-sm"
+ rating={rating}
+ label={intl.formatMessage(
+ {
+ id: 'metric.has_rating_X',
+ },
+ { 0: value },
+ )}
+ />
+ </Tooltip>
+ );
+}
+
+export const StyledSeparator = styled(BasicSeparator)`
+ background-color: ${themeColor('projectCardBorder')};
+`;
+
+export default SoftwareImpactMeasureRating;
} from '../../../../helpers/mocks/quality-gates';
import { mockLoggedInUser, mockPeriod } from '../../../../helpers/testMocks';
import { renderComponent } from '../../../../helpers/testReactTestingUtils';
-import { byRole } from '../../../../helpers/testSelector';
+import { byLabelText, byRole } from '../../../../helpers/testSelector';
import { ComponentQualifier } from '../../../../types/component';
import { MetricKey, MetricType } from '../../../../types/metrics';
import {
type = MetricType.Percent;
} else if (/_rating$/.test(key)) {
type = MetricType.Rating;
+ } else if (
+ [
+ MetricKey.reliability_issues,
+ MetricKey.security_issues,
+ MetricKey.maintainability_issues,
+ ].includes(key as MetricKey)
+ ) {
+ type = MetricType.Data;
} else {
type = MetricType.Integer;
}
metrics.push(mockMetric({ key, id: key, name: key, type }));
- measures.push(
- mockMeasure({
- metric: key,
- ...(isDiffMetric(key) ? { leak: '1' } : { period: undefined }),
- }),
- );
+
+ const measure = mockMeasure({
+ metric: key,
+ ...(isDiffMetric(key) ? { leak: '1' } : { period: undefined }),
+ });
+
+ // Mock software quality measures
+ if (type === MetricType.Data) {
+ if (key === MetricKey.reliability_issues) {
+ measure.value = JSON.stringify({
+ total: 3,
+ high: 0,
+ medium: 2,
+ low: 1,
+ });
+ } else if (key === MetricKey.maintainability_issues) {
+ measure.value = JSON.stringify({
+ total: 2,
+ high: 0,
+ medium: 0,
+ low: 1,
+ });
+ } else {
+ measure.value = JSON.stringify({
+ total: 0,
+ high: 0,
+ medium: 0,
+ low: 0,
+ });
+ }
+ }
+ // Mock software quality rating
+ if (key === MetricKey.reliability_rating) {
+ measure.value = '3';
+ } else if (key === MetricKey.security_rating) {
+ measure.value = '2';
+ } else if (key.endsWith('_rating') || key === MetricKey.sqale_rating) {
+ measure.value = '1';
+ }
+ measures.push(measure);
});
return Promise.resolve({
component: {
screen.queryByText('overview.quality_gate.conditions.cayc.warning'),
).not.toBeInTheDocument();
- //Measures panel
+ // Measures panel
expect(screen.getByText('overview.new_issues')).toBeInTheDocument();
expect(
byRole('link', {
}).get(),
).toBeInTheDocument();
- // go to overall
+ // Go to overall
await user.click(screen.getByText('overview.overall_code'));
expect(screen.getByText('metric.vulnerabilities.name')).toBeInTheDocument();
name: 'overview.see_more_details_on_x_of_y.1.metric.high_impact_accepted_issues.name',
}).get(),
).toBeInTheDocument();
+
+ // Software breakdown links should be correct
+ expect(
+ byRole('link', {
+ name: 'overview.measures.software_impact.see_list_of_x_open_issues.3.software_quality.RELIABILITY',
+ }).get(),
+ ).toHaveAttribute(
+ 'href',
+ '/project/issues?issueStatuses=OPEN%2CCONFIRMED&impactSoftwareQualities=RELIABILITY&id=foo',
+ );
+ expect(
+ byRole('link', {
+ name: 'overview.measures.software_impact.severity.see_x_open_issues.2.software_quality.RELIABILITY.overview.measures.software_impact.severity.MEDIUM.tooltip',
+ }).get(),
+ ).toHaveAttribute(
+ 'href',
+ '/project/issues?issueStatuses=OPEN%2CCONFIRMED&impactSoftwareQualities=RELIABILITY&impactSeverities=MEDIUM&id=foo',
+ );
+
+ // Active severity card should be the one with the highest severity
+ expect(
+ byRole('link', {
+ name: 'overview.measures.software_impact.severity.see_x_open_issues.2.software_quality.RELIABILITY.overview.measures.software_impact.severity.MEDIUM.tooltip',
+ }).get(),
+ ).toHaveClass('active', 'MEDIUM');
+
+ // Ratings should be correct and rendered
+ expect(byLabelText('metric.has_rating_X.C').get()).toBeInTheDocument();
+ expect(byLabelText('metric.has_rating_X.B').get()).toBeInTheDocument();
+ expect(byLabelText('metric.has_rating_X.A').getAll()).toHaveLength(2);
});
it('should show a successful non-compliant QG', async () => {
import { translate } from '../../helpers/l10n';
import { formatMeasure } from '../../helpers/measures';
import { parseAsString } from '../../helpers/query';
+import {
+ SoftwareImpactMeasureData,
+ SoftwareImpactSeverity,
+ SoftwareQuality,
+} from '../../types/clean-code-taxonomy';
import { IssueType } from '../../types/issues';
import { MetricKey } from '../../types/metrics';
import { AnalysisMeasuresVariations, MeasureHistory } from '../../types/project-activity';
MetricKey.duplicated_lines_density,
];
+export function softwareQualityToMeasure(softwareQuality: SoftwareQuality): MetricKey {
+ return (softwareQuality.toLowerCase() + '_issues') as MetricKey;
+}
+
+// Extract the number of issues for a given severity in the software impact measure
+export function getSoftwareImpactSeverityValue(
+ severity: SoftwareImpactSeverity,
+ softwareImpactMeasure?: SoftwareImpactMeasureData,
+) {
+ const key = severity.toLowerCase() as keyof SoftwareImpactMeasureData;
+ return softwareImpactMeasure ? softwareImpactMeasure[key]?.toString() : '';
+}
+
export function getIssueRatingName(type: IssueType) {
return translate('metric_domain', ISSUETYPE_METRIC_KEYS_MAP[type].ratingName);
}
value: number | string;
}
-export function RatingTooltipContent(props: RatingTooltipContentProps) {
+export function RatingTooltipContent(props: Readonly<RatingTooltipContentProps>) {
const {
appState: { settings },
metricKey,
const rating = Number(value);
const ratingLetter = formatMeasure(value, MetricType.Rating);
- if (finalMetricKey !== 'sqale_rating' && finalMetricKey !== 'maintainability_rating') {
+ if (finalMetricKey !== MetricKey.sqale_rating && finalMetricKey !== 'maintainability_rating') {
return <>{translate('metric', finalMetricKey, 'tooltip', ratingLetter)}</>;
}
softwareQuality: SoftwareQuality;
severity: SoftwareImpactSeverity;
}
+
+export interface SoftwareImpactMeasureData {
+ total: number;
+ high: number;
+ medium: number;
+ low: number;
+}
or=Or
open=Open
open_in_ide=Open in IDE
+open_issues=Open issues
optional=Optional
order=Order
owner=Owner
overview.measures.bad_reference.explanation=This branch could not be compared to its reference branch. See the SCM or analysis report for more details.
overview.measures.bad_setting.link=This can be fixed in the {setting_link} setting.
overview.measures.security_hotspots_reviewed=Reviewed
+overview.measures.software_impact.total_open_issues=Open issues
+overview.measures.software_impact.see_list_of_x_open_issues=See the list of {count} open {softwareQuality} issues
+overview.measures.software_impact.severity.see_x_open_issues=See {count} open {softwareQuality} issues with {severity} severity
+overview.measures.software_impact.severity.HIGH=H
+overview.measures.software_impact.severity.HIGH.tooltip=High Impact
+overview.measures.software_impact.severity.HIGH.improve_tooltip=high
+overview.measures.software_impact.severity.MEDIUM=M
+overview.measures.software_impact.severity.MEDIUM.tooltip=Medium Impact
+overview.measures.software_impact.severity.MEDIUM.improve_tooltip=medium
+overview.measures.software_impact.severity.LOW=L
+overview.measures.software_impact.severity.LOW.tooltip=Low Impact
+overview.measures.software_impact.severity.LOW.improve_tooltip=low
+overview.measures.software_impact.improve_rating_tooltip.title=Improve rating by fixing the highest severity issues first
+overview.measures.software_impact.improve_rating_tooltip.content.1={softwareQuality} rating is a {ratingLabel} when there is at least one {severity} impact vulnerability.
+overview.measures.software_impact.improve_rating_tooltip.content.2=To improve your rating, start fixing the issues with highest severity first.
overview.project.no_lines_of_code=This project has no lines of code.
overview.project.empty=This project is empty.