From a8336afac1068fdc4f79f65284f52511ca471964 Mon Sep 17 00:00:00 2001 From: stanislavh Date: Mon, 18 Dec 2023 14:11:03 +0100 Subject: [PATCH] SONAR-21280 Branch overview shows accepted issues --- .../components/icons/HighImpactCircleIcon.tsx | 45 ++++++ .../src/components/icons/index.ts | 1 + server/sonar-web/src/main/js/api/measures.ts | 30 +++- .../js/apps/issues/sidebar/PeriodFilter.tsx | 4 +- .../overview/branches/AcceptedIssuesPanel.tsx | 132 +++++++++++++++++ .../branches/BranchOverviewRenderer.tsx | 65 +++++++-- .../apps/overview/branches/MeasuresPanel.tsx | 133 +++++++----------- .../branches/__tests__/BranchOverview-it.tsx | 11 ++ .../BranchQualityGateConditions.tsx | 2 +- .../IssueMeasuresCardInner.tsx | 20 ++- .../pullRequests/IssueMeasuresCard.tsx | 5 +- .../src/main/js/apps/overview/utils.tsx | 10 ++ server/sonar-web/src/main/js/types/metrics.ts | 1 + .../resources/org/sonar/l10n/core.properties | 6 +- 14 files changed, 359 insertions(+), 106 deletions(-) create mode 100644 server/sonar-web/design-system/src/components/icons/HighImpactCircleIcon.tsx create mode 100644 server/sonar-web/src/main/js/apps/overview/branches/AcceptedIssuesPanel.tsx rename server/sonar-web/src/main/js/apps/overview/{pullRequests => components}/IssueMeasuresCardInner.tsx (80%) diff --git a/server/sonar-web/design-system/src/components/icons/HighImpactCircleIcon.tsx b/server/sonar-web/design-system/src/components/icons/HighImpactCircleIcon.tsx new file mode 100644 index 00000000000..d869ab5f13e --- /dev/null +++ b/server/sonar-web/design-system/src/components/icons/HighImpactCircleIcon.tsx @@ -0,0 +1,45 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 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 { useTheme } from '@emotion/react'; +import { themeColor, themeContrast } from '../../helpers'; +import { CustomIcon, IconProps } from './Icon'; + +export function HighImpactCircleIcon(props: Readonly) { + const theme = useTheme(); + + const bgColor = themeColor('overviewCardErrorIcon')({ + theme, + }); + const iconColor = themeContrast('overviewCardErrorIcon')({ + theme, + }); + + return ( + + + + + ); +} diff --git a/server/sonar-web/design-system/src/components/icons/index.ts b/server/sonar-web/design-system/src/components/icons/index.ts index 0efa2dea06c..1a5e151422a 100644 --- a/server/sonar-web/design-system/src/components/icons/index.ts +++ b/server/sonar-web/design-system/src/components/icons/index.ts @@ -39,6 +39,7 @@ export { FlagInfoIcon } from './FlagInfoIcon'; export { FlagSuccessIcon } from './FlagSuccessIcon'; export { FlagWarningIcon } from './FlagWarningIcon'; export { HelperHintIcon } from './HelperHintIcon'; +export { HighImpactCircleIcon } from './HighImpactCircleIcon'; export { HomeFillIcon } from './HomeFillIcon'; export { HomeIcon } from './HomeIcon'; export * from './Icon'; diff --git a/server/sonar-web/src/main/js/api/measures.ts b/server/sonar-web/src/main/js/api/measures.ts index 8df28f012e0..98eb5d0539a 100644 --- a/server/sonar-web/src/main/js/api/measures.ts +++ b/server/sonar-web/src/main/js/api/measures.ts @@ -108,17 +108,41 @@ export function getMeasuresWithPeriod( }).catch(throwGlobalError); } -export function getMeasuresWithPeriodAndMetrics( +export async function getMeasuresWithPeriodAndMetrics( component: string, metrics: string[], branchParameters?: BranchParameters, ): Promise { - return getJSON(COMPONENT_URL, { + // TODO: Remove this mock (SONAR-21323&SONAR-21275) + const mockedMetrics = metrics.filter( + (metric) => + metric !== MetricKey.new_accepted_issues && metric !== MetricKey.high_impact_accepted_issues, + ); + const result = await getJSON(COMPONENT_URL, { additionalFields: 'period,metrics', component, - metricKeys: metrics.join(','), + metricKeys: mockedMetrics.join(','), ...branchParameters, }).catch(throwGlobalError); + if (metrics.includes(MetricKey.high_impact_accepted_issues)) { + result.metrics.push({ + key: MetricKey.high_impact_accepted_issues, + name: 'Accepted Issues with high impact', + description: 'Accepted Issues with high impact', + domain: 'Reliability', + type: MetricType.Integer, + higherValuesAreBetter: false, + qualitative: true, + hidden: false, + bestValue: '0', + }); + result.component.measures?.push({ + metric: MetricKey.high_impact_accepted_issues, + value: '3', + }); + } + + return result; } export function getMeasuresForProjects( diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/PeriodFilter.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/PeriodFilter.tsx index 7d5bf439d97..4768f867260 100644 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/PeriodFilter.tsx +++ b/server/sonar-web/src/main/js/apps/issues/sidebar/PeriodFilter.tsx @@ -20,7 +20,7 @@ import { BasicSeparator, FacetItem } from 'design-system'; import * as React from 'react'; import { translate } from '../../../helpers/l10n'; -import { MeasuresPanelTabs } from '../../overview/branches/MeasuresPanel'; +import { MeasuresTabs } from '../../overview/utils'; import { Query } from '../utils'; import { FacetItemsList } from './FacetItemsList'; @@ -56,7 +56,7 @@ export function PeriodFilter(props: PeriodFilterProps) { className="it__search-navigator-facet" name={translate('issues.new_code')} onClick={handleClick} - value={newCodeSelected ? MeasuresPanelTabs.New : MeasuresPanelTabs.Overall} + value={newCodeSelected ? MeasuresTabs.New : MeasuresTabs.Overall} /> diff --git a/server/sonar-web/src/main/js/apps/overview/branches/AcceptedIssuesPanel.tsx b/server/sonar-web/src/main/js/apps/overview/branches/AcceptedIssuesPanel.tsx new file mode 100644 index 00000000000..a03ea74eec2 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/overview/branches/AcceptedIssuesPanel.tsx @@ -0,0 +1,132 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 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 { + Card, + HighImpactCircleIcon, + LightLabel, + PageTitle, + SnoozeCircleIcon, + Spinner, + themeColor, +} from 'design-system'; +import * as React from 'react'; +import { useIntl } from 'react-intl'; +import { getLeakValue } from '../../../components/measure/utils'; +import { DEFAULT_ISSUES_QUERY } from '../../../components/shared/utils'; +import { getBranchLikeQuery } from '../../../helpers/branch-like'; +import { findMeasure, formatMeasure } from '../../../helpers/measures'; +import { getComponentIssuesUrl } from '../../../helpers/urls'; +import { Branch } from '../../../types/branch-like'; +import { SoftwareImpactSeverity } from '../../../types/clean-code-taxonomy'; +import { IssueStatus } from '../../../types/issues'; +import { MetricKey, MetricType } from '../../../types/metrics'; +import { Component, MeasureEnhanced } from '../../../types/types'; +import { IssueMeasuresCardInner } from '../components/IssueMeasuresCardInner'; + +export interface AcceptedIssuesPanelProps { + branch?: Branch; + component: Component; + measures?: MeasureEnhanced[]; + isNewCode: boolean; + loading?: boolean; +} + +function AcceptedIssuesPanel(props: Readonly) { + const { branch, component, measures = [], isNewCode, loading } = props; + const intl = useIntl(); + + const acceptedIssuesUrl = getComponentIssuesUrl(component.key, { + ...getBranchLikeQuery(branch), + issueStatuses: IssueStatus.Accepted, + ...(isNewCode ? { inNewCodePeriod: 'true' } : {}), + }); + + const acceptedIssuesWithHighImpactUrl = getComponentIssuesUrl(component.key, { + ...getBranchLikeQuery(branch), + ...DEFAULT_ISSUES_QUERY, + issueStatuses: IssueStatus.Accepted, + impactSeverities: SoftwareImpactSeverity.High, + }); + + const acceptedCount = isNewCode + ? getLeakValue(findMeasure(measures, MetricKey.new_accepted_issues)) + : findMeasure(measures, MetricKey.accepted_issues)?.value; + + const acceptedWithHighImpactCount = isNewCode + ? undefined + : findMeasure(measures, MetricKey.high_impact_accepted_issues)?.value; + + return ( +
+ + + {intl.formatMessage({ id: 'overview.accepted_issues.description' })} + + +
+ + + } + /> + {!isNewCode && ( + <> + + } + /> + + )} + +
+
+
+ ); +} + +const StyledCardSeparator = styled.div` + width: 1px; + background-color: ${themeColor('projectCardBorder')}; +`; + +export default React.memo(AcceptedIssuesPanel); diff --git a/server/sonar-web/src/main/js/apps/overview/branches/BranchOverviewRenderer.tsx b/server/sonar-web/src/main/js/apps/overview/branches/BranchOverviewRenderer.tsx index 469dfc60a99..6fe2a9dca43 100644 --- a/server/sonar-web/src/main/js/apps/overview/branches/BranchOverviewRenderer.tsx +++ b/server/sonar-web/src/main/js/apps/overview/branches/BranchOverviewRenderer.tsx @@ -20,16 +20,22 @@ import { LargeCenteredLayout, PageContentFontWrapper } from 'design-system'; import * as React from 'react'; import A11ySkipTarget from '../../../components/a11y/A11ySkipTarget'; +import { useLocation } from '../../../components/hoc/withRouter'; import { parseDate } from '../../../helpers/dates'; +import { isDiffMetric } from '../../../helpers/measures'; +import { CodeScope } from '../../../helpers/urls'; import { ApplicationPeriod } from '../../../types/application'; import { Branch } from '../../../types/branch-like'; import { ComponentQualifier } from '../../../types/component'; import { Analysis, GraphType, MeasureHistory } from '../../../types/project-activity'; import { QualityGateStatus } from '../../../types/quality-gates'; import { Component, MeasureEnhanced, Metric, Period, QualityGate } from '../../../types/types'; +import { MeasuresTabs } from '../utils'; +import AcceptedIssuesPanel from './AcceptedIssuesPanel'; import ActivityPanel from './ActivityPanel'; import FirstAnalysisNextStepsNotif from './FirstAnalysisNextStepsNotif'; import MeasuresPanel from './MeasuresPanel'; +import MeasuresPanelNoNewCode from './MeasuresPanelNoNewCode'; import NoCodeWarning from './NoCodeWarning'; import QualityGatePanel from './QualityGatePanel'; @@ -64,7 +70,7 @@ export default function BranchOverviewRenderer(props: BranchOverviewRendererProp graph, loadingHistory, loadingStatus, - measures, + measures = [], measuresHistory = [], metrics = [], onGraphChange, @@ -74,7 +80,24 @@ export default function BranchOverviewRenderer(props: BranchOverviewRendererProp qualityGate, } = props; + const { query } = useLocation(); + const [tab, selectTab] = React.useState(() => { + return query.codeScope === CodeScope.Overall ? MeasuresTabs.Overall : MeasuresTabs.New; + }); + const leakPeriod = component.qualifier === ComponentQualifier.Application ? appLeak : period; + const isNewCodeTab = tab === MeasuresTabs.New; + const hasNewCodeMeasures = measures.some((m) => isDiffMetric(m.metric.key)); + + React.useEffect(() => { + // Open Overall tab by default if there are no new measures. + if (loadingStatus === false && !hasNewCodeMeasures && isNewCodeTab) { + selectTab(MeasuresTabs.Overall); + } + // In this case, we explicitly do NOT want to mark tab as a dependency, as + // it would prevent the user from selecting it, even if it's empty. + /* eslint-disable-next-line react-hooks/exhaustive-deps */ + }, [loadingStatus, hasNewCodeMeasures]); return ( <> @@ -103,16 +126,36 @@ export default function BranchOverviewRenderer(props: BranchOverviewRendererProp
- + {!hasNewCodeMeasures && isNewCodeTab && !loadingStatus ? ( + + ) : ( + <> + + + + + )} void; } const SQ_UPGRADE_NOTIFICATION_TIMEOUT = { weeks: 3 }; @@ -79,29 +72,19 @@ export function MeasuresPanel(props: MeasuresPanelProps) { branch, component, loading, - measures = [], + measures, period, qgStatuses = [], - location, + isNewCode, } = props; - const hasDiffMeasures = measures.some((m) => isDiffMetric(m.metric.key)); const isApp = component.qualifier === ComponentQualifier.Application; const leakPeriod = isApp ? appLeak : period; - const query = parseQuery(location.query); const { failingConditionsOnNewCode, failingConditionsOnOverallCode } = countFailingConditions(qgStatuses); const failingConditions = failingConditionsOnNewCode + failingConditionsOnOverallCode; - const [tab, selectTab] = React.useState(() => { - return query.codeScope === CodeScope.Overall - ? MeasuresPanelTabs.Overall - : MeasuresPanelTabs.New; - }); - - const isNewCodeTab = tab === MeasuresPanelTabs.New; - const recentSqUpgradeEvent = React.useMemo(() => { if (!analyses || analyses.length === 0) { return undefined; @@ -139,24 +122,14 @@ export function MeasuresPanel(props: MeasuresPanelProps) { }); }; - React.useEffect(() => { - // Open Overall tab by default if there are no new measures. - if (loading === false && !hasDiffMeasures && isNewCodeTab) { - selectTab(MeasuresPanelTabs.Overall); - } - // In this case, we explicitly do NOT want to mark tab as a dependency, as - // it would prevent the user from selecting it, even if it's empty. - /* eslint-disable-next-line react-hooks/exhaustive-deps */ - }, [loading, hasDiffMeasures]); - const tabs = [ { - value: MeasuresPanelTabs.New, + value: MeasuresTabs.New, label: translate('overview.new_code'), counter: failingConditionsOnNewCode, }, { - value: MeasuresPanelTabs.Overall, + value: MeasuresTabs.Overall, label: translate('overview.overall_code'), counter: failingConditionsOnOverallCode, }, @@ -196,7 +169,11 @@ export function MeasuresPanel(props: MeasuresPanelProps) {
)}
- selectTab(key)} options={tabs} value={tab} /> + {failingConditions > 0 && ( {failingConditions === 1 @@ -205,7 +182,7 @@ export function MeasuresPanel(props: MeasuresPanelProps) { )}
- {tab === MeasuresPanelTabs.New && leakPeriod ? ( + {isNewCode && leakPeriod ? ( {translate('overview.new_code')}: @@ -230,62 +207,58 @@ export function MeasuresPanel(props: MeasuresPanelProps) { )} - {!hasDiffMeasures && isNewCodeTab ? ( - - ) : ( -
- {[ - IssueType.Bug, - IssueType.CodeSmell, - IssueType.Vulnerability, - IssueType.SecurityHotspot, - ].map((type: IssueType) => ( - - - - ))} - - {(findMeasure(measures, MetricKey.coverage) || - findMeasure(measures, MetricKey.new_coverage)) && ( - - - - )} +
+ {[ + IssueType.Bug, + IssueType.CodeSmell, + IssueType.Vulnerability, + IssueType.SecurityHotspot, + ].map((type: IssueType) => ( + + + + ))} - + {(findMeasure(measures, MetricKey.coverage) || + findMeasure(measures, MetricKey.new_coverage)) && ( + -
- )} + )} + + + + +
)}
); } -export default withRouter(React.memo(MeasuresPanel)); +export default React.memo(MeasuresPanel); function renderCoverageIcon(value?: string) { return ; diff --git a/server/sonar-web/src/main/js/apps/overview/branches/__tests__/BranchOverview-it.tsx b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/BranchOverview-it.tsx index dd2737d434c..b074b53a63b 100644 --- a/server/sonar-web/src/main/js/apps/overview/branches/__tests__/BranchOverview-it.tsx +++ b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/BranchOverview-it.tsx @@ -37,6 +37,7 @@ import { } from '../../../../helpers/mocks/quality-gates'; import { mockLoggedInUser, mockPeriod } from '../../../../helpers/testMocks'; import { renderComponent } from '../../../../helpers/testReactTestingUtils'; +import { byRole } from '../../../../helpers/testSelector'; import { ComponentQualifier } from '../../../../types/component'; import { MetricKey, MetricType } from '../../../../types/metrics'; import { @@ -218,11 +219,21 @@ describe('project overview', () => { //Measures panel expect(screen.getByText('metric.new_vulnerabilities.name')).toBeInTheDocument(); + expect( + byRole('link', { + name: 'overview.see_more_details_on_x_of_y.1.metric.accepted_issues.name', + }).get(), + ).toBeInTheDocument(); // go to overall await user.click(screen.getByText('overview.overall_code')); expect(screen.getByText('metric.vulnerabilities.name')).toBeInTheDocument(); + expect( + byRole('link', { + name: 'overview.see_more_details_on_x_of_y.1.metric.high_impact_accepted_issues.name', + }).get(), + ).toBeInTheDocument(); }); it('should show a successful non-compliant QG', async () => { diff --git a/server/sonar-web/src/main/js/apps/overview/components/BranchQualityGateConditions.tsx b/server/sonar-web/src/main/js/apps/overview/components/BranchQualityGateConditions.tsx index cac37b8347a..d3bfaa0bdad 100644 --- a/server/sonar-web/src/main/js/apps/overview/components/BranchQualityGateConditions.tsx +++ b/server/sonar-web/src/main/js/apps/overview/components/BranchQualityGateConditions.tsx @@ -59,7 +59,7 @@ export default function BranchQualityGateConditions(props: Readonly) { ); return ( -
    +
      {filteredFailedConditions.map((condition) => (
    • diff --git a/server/sonar-web/src/main/js/apps/overview/pullRequests/IssueMeasuresCardInner.tsx b/server/sonar-web/src/main/js/apps/overview/components/IssueMeasuresCardInner.tsx similarity index 80% rename from server/sonar-web/src/main/js/apps/overview/pullRequests/IssueMeasuresCardInner.tsx rename to server/sonar-web/src/main/js/apps/overview/components/IssueMeasuresCardInner.tsx index ccdf8c965f3..3c6e7efd861 100644 --- a/server/sonar-web/src/main/js/apps/overview/pullRequests/IssueMeasuresCardInner.tsx +++ b/server/sonar-web/src/main/js/apps/overview/components/IssueMeasuresCardInner.tsx @@ -17,7 +17,9 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { Badge, ContentLink } from 'design-system'; +import styled from '@emotion/styled'; +import classNames from 'classnames'; +import { Badge, ContentLink, themeColor } from 'design-system'; import * as React from 'react'; import { Path } from 'react-router-dom'; import { translate, translateWithParameters } from '../../../helpers/l10n'; @@ -35,12 +37,12 @@ interface IssueMeasuresCardInnerProps extends React.HTMLAttributes) { - const { header, metric, icon, value, url, failed, footer, ...rest } = props; + const { header, metric, icon, value, url, failed, footer, className, ...rest } = props; return ( -
      +
      -
      + {header} {failed && ( @@ -48,19 +50,19 @@ export function IssueMeasuresCardInner(props: Readonly )} -
      +
      - {value ?? '0'} + {value || '0'}
      @@ -71,3 +73,7 @@ export function IssueMeasuresCardInner(props: Readonly ); } + +const ColorBold = styled.div` + color: ${themeColor('pageTitle')}; +`; 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 index 99225368fcf..12efb13cf87 100644 --- a/server/sonar-web/src/main/js/apps/overview/pullRequests/IssueMeasuresCard.tsx +++ b/server/sonar-web/src/main/js/apps/overview/pullRequests/IssueMeasuresCard.tsx @@ -42,8 +42,8 @@ import { PullRequest } from '../../../types/branch-like'; import { MetricKey, MetricType } from '../../../types/metrics'; import { QualityGateStatusConditionEnhanced } from '../../../types/quality-gates'; import { Component, MeasureEnhanced } from '../../../types/types'; +import { IssueMeasuresCardInner } from '../components/IssueMeasuresCardInner'; import { getConditionRequiredLabel, Status } from '../utils'; -import { IssueMeasuresCardInner } from './IssueMeasuresCardInner'; interface Props { conditions: QualityGateStatusConditionEnhanced[]; @@ -82,6 +82,7 @@ export default function IssueMeasuresCard( return ( {intl.formatMessage({ id: 'overview.pull_request.fixed_issues' })} diff --git a/server/sonar-web/src/main/js/apps/overview/utils.tsx b/server/sonar-web/src/main/js/apps/overview/utils.tsx index a3816df71a4..47021a519c5 100644 --- a/server/sonar-web/src/main/js/apps/overview/utils.tsx +++ b/server/sonar-web/src/main/js/apps/overview/utils.tsx @@ -37,6 +37,11 @@ export const METRICS: string[] = [ MetricKey.alert_status, MetricKey.quality_gate_details, // TODO: still relevant? + // issues + MetricKey.accepted_issues, + MetricKey.new_accepted_issues, + MetricKey.high_impact_accepted_issues, + // bugs MetricKey.bugs, MetricKey.new_bugs, @@ -125,6 +130,11 @@ const MEASURES_VARIATIONS_METRICS = [ MetricKey.vulnerabilities, ]; +export enum MeasuresTabs { + New = 'new', + Overall = 'overall', +} + export enum MeasurementType { Coverage = 'COVERAGE', Duplication = 'DUPLICATION', diff --git a/server/sonar-web/src/main/js/types/metrics.ts b/server/sonar-web/src/main/js/types/metrics.ts index f1b6edd1f0d..043b54fd979 100644 --- a/server/sonar-web/src/main/js/types/metrics.ts +++ b/server/sonar-web/src/main/js/types/metrics.ts @@ -155,6 +155,7 @@ export enum MetricKey { violations = 'violations', vulnerabilities = 'vulnerabilities', accepted_issues = 'accepted_issues', + high_impact_accepted_issues = 'high_impact_accepted_issues', wont_fix_issues = 'wont_fix_issues', } diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index f43ef32e670..cbd79aa6161 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -3268,7 +3268,8 @@ metric.pull_request_fixed_issues.name=Fixed issues metric.pull_request_fixed_issues.description=Fixed issues metric.new_accepted_issues.name=Accepted issues metric.new_accepted_issues.description=Accepted issues - +metric.high_impact_accepted_issues.name=High impact accepted issues +metric.high_impact_accepted_issues.description=High impact accepted issues #------------------------------------------------------------------------------ # # PERMISSIONS @@ -3915,6 +3916,9 @@ overview.project_key.click_to_copy=Click to copy the key to your clipboard overview.activity=Activity overview.activity.graph_shows_data_for_x=This graph shows historical data for {0}. Click on the "Activity" link below to see more information. overview.recent_activity=Recent Activity +overview.accepted_issues=Accepted issues +overview.accepted_issues.description=Issues that are valid, but were not fixed and represent accepted technical debt. +overview.accepted_issues.total=Total accepted issues overview.measures=Measures overview.measures.empty_explanation=Measures on New Code will appear after the second analysis of this branch. overview.measures.empty_link={learn_more_link} about the Clean as You Code approach. -- 2.39.5