+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.
- */
-// @flow
-import React from 'react';
-import Tooltip from '../../../components/controls/Tooltip';
-import DateTooltipFormatter from '../../../components/intl/DateTooltipFormatter';
-import { getApplicationLeak } from '../../../api/application';
-import { translate } from '../../../helpers/l10n';
-
-/*::
-type Props = {
- component: { key: string }
-};
-*/
-
-/*::
-type State = {
- leaks: ?Array<{ date: string, project: string, projectName: string }>
-};
-*/
-
-export default class ApplicationLeakPeriodLegend extends React.Component {
- /*:: mounted: boolean; */
- /*:: props: Props; */
- state /*: State */ = {
- leaks: null
- };
-
- componentDidMount() {
- this.mounted = true;
- }
-
- componentWillReceiveProps(nextProps /*: Props */) {
- if (nextProps.component.key !== this.props.component.key) {
- this.setState({ leaks: null });
- }
- }
-
- componentWillUnmount() {
- this.mounted = false;
- }
-
- fetchLeaks = (visible /*: boolean */) => {
- if (visible && this.state.leaks == null) {
- getApplicationLeak(this.props.component.key).then(
- leaks => {
- if (this.mounted) {
- this.setState({ leaks });
- }
- },
- () => {
- if (this.mounted) {
- this.setState({ leaks: [] });
- }
- }
- );
- }
- };
-
- renderOverlay = () =>
- this.state.leaks != null ? (
- <ul className="text-left">
- {this.state.leaks.map(leak => (
- <li key={leak.project}>
- {leak.projectName}: <DateTooltipFormatter date={leak.date} />
- </li>
- ))}
- </ul>
- ) : (
- <i className="spinner spinner-margin" />
- );
-
- render() {
- return (
- <Tooltip onVisibleChange={this.fetchLeaks} overlay={this.renderOverlay()}>
- <div className="overview-legend overview-legend-spaced-line">
- {translate('issues.leak_period')}
- </div>
- </Tooltip>
- );
- }
-}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 Tooltip from '../../../components/controls/Tooltip';
+import DateTooltipFormatter from '../../../components/intl/DateTooltipFormatter';
+import { getApplicationLeak } from '../../../api/application';
+import { translate } from '../../../helpers/l10n';
+
+interface Props {
+ component: string;
+}
+
+interface State {
+ leaks?: Array<{ date: string; project: string; projectName: string }>;
+}
+
+export default class ApplicationLeakPeriodLegend extends React.Component<Props, State> {
+ mounted = false;
+ state: State = {};
+
+ componentDidMount() {
+ this.mounted = true;
+ }
+
+ componentWillReceiveProps(nextProps: Props) {
+ if (nextProps.component !== this.props.component) {
+ this.setState({ leaks: undefined });
+ }
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
+ fetchLeaks = (visible: boolean) => {
+ if (visible && this.state.leaks) {
+ getApplicationLeak(this.props.component).then(
+ leaks => {
+ if (this.mounted) {
+ this.setState({ leaks });
+ }
+ },
+ () => {
+ if (this.mounted) {
+ this.setState({ leaks: [] });
+ }
+ }
+ );
+ }
+ };
+
+ renderOverlay = () =>
+ this.state.leaks != null ? (
+ <ul className="text-left">
+ {this.state.leaks.map(leak => (
+ <li key={leak.project}>
+ {leak.projectName}: <DateTooltipFormatter date={leak.date} />
+ </li>
+ ))}
+ </ul>
+ ) : (
+ <i className="spinner spinner-margin" />
+ );
+
+ render() {
+ return (
+ <Tooltip onVisibleChange={this.fetchLeaks} overlay={this.renderOverlay()}>
+ <div className="overview-legend overview-legend-spaced-line">
+ {translate('issues.leak_period')}
+ </div>
+ </Tooltip>
+ );
+ }
+}
};
getApplicationLeakPeriod = () =>
- this.state.measures.find(measure => measure.metric.key === 'new_bugs') ? { index: 1 } : null;
+ this.state.measures.find(measure => measure.metric.key === 'new_bugs')
+ ? { index: 1 }
+ : undefined;
renderLoading() {
return (
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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.
- */
-// @flow
-import React from 'react';
-import { shallow } from 'enzyme';
-import ApplicationLeakPeriodLegend from '../ApplicationLeakPeriodLegend';
-
-it('renders', () => {
- const wrapper = shallow(<ApplicationLeakPeriodLegend component={{ key: 'foo' }} />);
- expect(wrapper).toMatchSnapshot();
- wrapper.setState({
- leaks: [
- { date: '2017-01-01T11:39:03+0100', project: 'foo', projectName: 'Foo' },
- { date: '2017-02-01T11:39:03+0100', project: 'bar', projectName: 'Bar' }
- ]
- });
- expect(wrapper).toMatchSnapshot();
-});
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 { shallow } from 'enzyme';
+import ApplicationLeakPeriodLegend from '../ApplicationLeakPeriodLegend';
+
+it('renders', () => {
+ const wrapper = shallow(<ApplicationLeakPeriodLegend component="foo" />);
+ expect(wrapper).toMatchSnapshot();
+ wrapper.setState({
+ leaks: [
+ { date: '2017-01-01T11:39:03+0100', project: 'foo', projectName: 'Foo' },
+ { date: '2017-02-01T11:39:03+0100', project: 'bar', projectName: 'Bar' }
+ ]
+ });
+ expect(wrapper).toMatchSnapshot();
+});
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`renders 1`] = `
-<Tooltip
- onVisibleChange={[Function]}
- overlay={
- <i
- className="spinner spinner-margin"
- />
- }
- placement="bottom"
->
- <div
- className="overview-legend overview-legend-spaced-line"
- >
- issues.leak_period
- </div>
-</Tooltip>
-`;
-
-exports[`renders 2`] = `
-<Tooltip
- onVisibleChange={[Function]}
- overlay={
- <ul
- className="text-left"
- >
- <li>
- Foo
- :
- <DateTooltipFormatter
- date="2017-01-01T11:39:03+0100"
- />
- </li>
- <li>
- Bar
- :
- <DateTooltipFormatter
- date="2017-02-01T11:39:03+0100"
- />
- </li>
- </ul>
- }
- placement="bottom"
->
- <div
- className="overview-legend overview-legend-spaced-line"
- >
- issues.leak_period
- </div>
-</Tooltip>
-`;
--- /dev/null
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<Tooltip
+ onVisibleChange={[Function]}
+ overlay={
+ <i
+ className="spinner spinner-margin"
+ />
+ }
+ placement="bottom"
+>
+ <div
+ className="overview-legend overview-legend-spaced-line"
+ >
+ issues.leak_period
+ </div>
+</Tooltip>
+`;
+
+exports[`renders 2`] = `
+<Tooltip
+ onVisibleChange={[Function]}
+ overlay={
+ <ul
+ className="text-left"
+ >
+ <li>
+ Foo
+ :
+ <DateTooltipFormatter
+ date="2017-01-01T11:39:03+0100"
+ />
+ </li>
+ <li>
+ Bar
+ :
+ <DateTooltipFormatter
+ date="2017-02-01T11:39:03+0100"
+ />
+ </li>
+ </ul>
+ }
+ placement="bottom"
+>
+ <div
+ className="overview-legend overview-legend-spaced-line"
+ >
+ issues.leak_period
+ </div>
+</Tooltip>
+`;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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 { Link } from 'react-router';
-import enhance from './enhance';
-import ApplicationLeakPeriodLegend from '../components/ApplicationLeakPeriodLegend';
-import BubblesIcon from '../../../components/icons-components/BubblesIcon';
-import BugIcon from '../../../components/icons-components/BugIcon';
-import LeakPeriodLegend from '../components/LeakPeriodLegend';
-import VulnerabilityIcon from '../../../components/icons-components/VulnerabilityIcon';
-import { getMetricName } from '../helpers/metrics';
-import { getComponentDrilldownUrl } from '../../../helpers/urls';
-import { translate } from '../../../helpers/l10n';
-
-class BugsAndVulnerabilities extends React.PureComponent {
- renderHeader() {
- const { branch, component } = this.props;
-
- return (
- <div className="overview-card-header">
- <div className="overview-title">
- <span>{translate('metric.bugs.name')}</span>
- <Link
- className="button button-small spacer-left text-text-bottom"
- to={getComponentDrilldownUrl(component.key, 'Reliability', branch)}>
- <BubblesIcon size={14} />
- </Link>
- <span className="big-spacer-left">{translate('metric.vulnerabilities.name')}</span>
- <Link
- className="button button-small spacer-left text-text-bottom"
- to={getComponentDrilldownUrl(component.key, 'Security', branch)}>
- <BubblesIcon size={14} />
- </Link>
- </div>
- </div>
- );
- }
-
- renderLeak() {
- const { component, leakPeriod } = this.props;
-
- if (leakPeriod == null) {
- return null;
- }
-
- return (
- <div className="overview-domain-leak">
- {component.qualifier === 'APP' ? (
- <ApplicationLeakPeriodLegend component={component} />
- ) : (
- <LeakPeriodLegend period={leakPeriod} />
- )}
-
- <div className="overview-domain-measures">
- <div className="overview-domain-measure">
- <div className="overview-domain-measure-value">
- <span style={{ marginLeft: 30 }}>{this.props.renderIssues('new_bugs', 'BUG')}</span>
- {this.props.renderRating('new_reliability_rating')}
- </div>
- <div className="overview-domain-measure-label">
- <BugIcon className="little-spacer-right" />
- {getMetricName('new_bugs')}
- </div>
- </div>
- <div className="overview-domain-measure">
- <div className="overview-domain-measure-value">
- <span style={{ marginLeft: 30 }}>
- {this.props.renderIssues('new_vulnerabilities', 'VULNERABILITY')}
- </span>
- {this.props.renderRating('new_security_rating')}
- </div>
- <div className="overview-domain-measure-label">
- <VulnerabilityIcon className="little-spacer-right" />
- {getMetricName('new_vulnerabilities')}
- </div>
- </div>
- </div>
- </div>
- );
- }
-
- renderNutshell() {
- return (
- <div className="overview-domain-nutshell">
- <div className="overview-domain-measures">
- <div className="overview-domain-measure">
- <div className="overview-domain-measure-value">
- {this.props.renderIssues('bugs', 'BUG')}
- {this.props.renderRating('reliability_rating')}
- </div>
- <div className="overview-domain-measure-label">
- <BugIcon className="little-spacer-right " />
- {getMetricName('bugs')}
- {this.props.renderHistoryLink('bugs')}
- </div>
- </div>
- <div className="overview-domain-measure">
- <div className="overview-domain-measure-value">
- {this.props.renderIssues('vulnerabilities', 'VULNERABILITY')}
- {this.props.renderRating('security_rating')}
- </div>
- <div className="overview-domain-measure-label">
- <VulnerabilityIcon className="little-spacer-right " />
- {getMetricName('vulnerabilities')}
- {this.props.renderHistoryLink('vulnerabilities')}
- </div>
- </div>
- </div>
- </div>
- );
- }
-
- render() {
- const { measures } = this.props;
- const bugsMeasure = measures.find(measure => measure.metric.key === 'bugs');
- if (bugsMeasure == null) {
- return null;
- }
- return (
- <div className="overview-card overview-card-special">
- {this.renderHeader()}
-
- <div className="overview-domain-panel">
- {this.renderNutshell()}
- {this.renderLeak()}
- </div>
- </div>
- );
- }
-}
-
-export default enhance(BugsAndVulnerabilities);
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 { Link } from 'react-router';
+import enhance, { ComposedProps } from './enhance';
+import ApplicationLeakPeriodLegend from '../components/ApplicationLeakPeriodLegend';
+import BubblesIcon from '../../../components/icons-components/BubblesIcon';
+import BugIcon from '../../../components/icons-components/BugIcon';
+import LeakPeriodLegend from '../components/LeakPeriodLegend';
+import VulnerabilityIcon from '../../../components/icons-components/VulnerabilityIcon';
+import { getMetricName } from '../helpers/metrics';
+import { getComponentDrilldownUrl } from '../../../helpers/urls';
+import { translate } from '../../../helpers/l10n';
+
+export class BugsAndVulnerabilities extends React.PureComponent<ComposedProps> {
+ renderHeader() {
+ const { branch, component } = this.props;
+
+ return (
+ <div className="overview-card-header">
+ <div className="overview-title">
+ <span>{translate('metric.bugs.name')}</span>
+ <Link
+ className="button button-small spacer-left text-text-bottom"
+ to={getComponentDrilldownUrl(component.key, 'Reliability', branch)}>
+ <BubblesIcon size={14} />
+ </Link>
+ <span className="big-spacer-left">{translate('metric.vulnerabilities.name')}</span>
+ <Link
+ className="button button-small spacer-left text-text-bottom"
+ to={getComponentDrilldownUrl(component.key, 'Security', branch)}>
+ <BubblesIcon size={14} />
+ </Link>
+ </div>
+ </div>
+ );
+ }
+
+ renderLeak() {
+ const { component, leakPeriod } = this.props;
+ if (!leakPeriod) {
+ return null;
+ }
+
+ return (
+ <div className="overview-domain-leak">
+ {component.qualifier === 'APP' ? (
+ <ApplicationLeakPeriodLegend component={component.key} />
+ ) : (
+ <LeakPeriodLegend period={leakPeriod} />
+ )}
+
+ <div className="overview-domain-measures">
+ <div className="overview-domain-measure">
+ <div className="overview-domain-measure-value">
+ <span style={{ marginLeft: 30 }}>{this.props.renderIssues('new_bugs', 'BUG')}</span>
+ {this.props.renderRating('new_reliability_rating')}
+ </div>
+ <div className="overview-domain-measure-label">
+ <BugIcon className="little-spacer-right" />
+ {getMetricName('new_bugs')}
+ </div>
+ </div>
+ <div className="overview-domain-measure">
+ <div className="overview-domain-measure-value">
+ <span style={{ marginLeft: 30 }}>
+ {this.props.renderIssues('new_vulnerabilities', 'VULNERABILITY')}
+ </span>
+ {this.props.renderRating('new_security_rating')}
+ </div>
+ <div className="overview-domain-measure-label">
+ <VulnerabilityIcon className="little-spacer-right" />
+ {getMetricName('new_vulnerabilities')}
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+ }
+
+ renderNutshell() {
+ return (
+ <div className="overview-domain-nutshell">
+ <div className="overview-domain-measures">
+ <div className="overview-domain-measure">
+ <div className="overview-domain-measure-value">
+ {this.props.renderIssues('bugs', 'BUG')}
+ {this.props.renderRating('reliability_rating')}
+ </div>
+ <div className="overview-domain-measure-label">
+ <BugIcon className="little-spacer-right " />
+ {getMetricName('bugs')}
+ {this.props.renderHistoryLink('bugs')}
+ </div>
+ </div>
+ <div className="overview-domain-measure">
+ <div className="overview-domain-measure-value">
+ {this.props.renderIssues('vulnerabilities', 'VULNERABILITY')}
+ {this.props.renderRating('security_rating')}
+ </div>
+ <div className="overview-domain-measure-label">
+ <VulnerabilityIcon className="little-spacer-right " />
+ {getMetricName('vulnerabilities')}
+ {this.props.renderHistoryLink('vulnerabilities')}
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+ }
+
+ render() {
+ const { measures } = this.props;
+ const bugsMeasure = measures.find(measure => measure.metric.key === 'bugs');
+ if (!bugsMeasure) {
+ return null;
+ }
+ return (
+ <div className="overview-card overview-card-special">
+ {this.renderHeader()}
+
+ <div className="overview-domain-panel">
+ {this.renderNutshell()}
+ {this.renderLeak()}
+ </div>
+ </div>
+ );
+ }
+}
+
+export default enhance(BugsAndVulnerabilities);
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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 { Link } from 'react-router';
-import enhance from './enhance';
-import Tooltip from '../../../components/controls/Tooltip';
-import DateFromNow from '../../../components/intl/DateFromNow';
-import DateTimeFormatter from '../../../components/intl/DateTimeFormatter';
-import { getMetricName } from '../helpers/metrics';
-import { translate, translateWithParameters } from '../../../helpers/l10n';
-import { formatMeasure, isDiffMetric } from '../../../helpers/measures';
-import { getComponentIssuesUrl } from '../../../helpers/urls';
-import CodeSmellIcon from '../../../components/icons-components/CodeSmellIcon';
-
-class CodeSmells extends React.PureComponent {
- renderHeader() {
- return this.props.renderHeader('Maintainability', translate('metric.code_smells.name'));
- }
-
- renderDebt(metric, type) {
- const { branch, measures, component } = this.props;
- const measure = measures.find(measure => measure.metric.key === metric);
- const value = this.props.getValue(measure);
- const params = { branch, resolved: 'false', facetMode: 'effort', types: type };
-
- if (isDiffMetric(metric)) {
- Object.assign(params, { sinceLeakPeriod: 'true' });
- }
-
- const tooltip = (
- <DateTimeFormatter date={component.analysisDate}>
- {formattedAnalysisDate => (
- <span>{translateWithParameters('widget.as_calculated_on_x', formattedAnalysisDate)}</span>
- )}
- </DateTimeFormatter>
- );
- return (
- <Tooltip overlay={tooltip} placement="top">
- <Link to={getComponentIssuesUrl(component.key, params)}>
- {formatMeasure(value, 'SHORT_WORK_DUR')}
- </Link>
- </Tooltip>
- );
- }
-
- renderTimelineStartDate() {
- if (!this.props.historyStartDate) {
- return null;
- }
- return (
- <DateFromNow date={this.props.historyStartDate}>
- {fromNow => (
- <span className="overview-domain-timeline-date">
- {translateWithParameters('overview.started_x', fromNow)}
- </span>
- )}
- </DateFromNow>
- );
- }
-
- renderTimeline(range, displayDate) {
- return this.props.renderTimeline(
- 'sqale_index',
- range,
- displayDate ? this.renderTimelineStartDate() : null
- );
- }
-
- renderLeak() {
- const { leakPeriod } = this.props;
-
- if (leakPeriod == null) {
- return null;
- }
-
- return (
- <div className="overview-domain-leak">
- <div className="overview-domain-measures">
- <div className="overview-domain-measure">
- <div className="overview-domain-measure-value">
- <span style={{ marginLeft: 30 }}>
- {this.renderDebt('new_technical_debt', 'CODE_SMELL')}
- </span>
- {this.props.renderRating('new_maintainability_rating')}
- </div>
- <div className="overview-domain-measure-label">{getMetricName('new_effort')}</div>
- </div>
- <div className="overview-domain-measure">
- <div className="overview-domain-measure-value">
- {this.props.renderIssues('new_code_smells', 'CODE_SMELL')}
- </div>
- <div className="overview-domain-measure-label">
- <CodeSmellIcon className="little-spacer-right" />
- {getMetricName('new_code_smells')}
- </div>
- </div>
- </div>
-
- {this.renderTimeline('after')}
- </div>
- );
- }
-
- renderNutshell() {
- return (
- <div className="overview-domain-nutshell">
- <div className="overview-domain-measures">
- <div className="overview-domain-measure">
- <div className="overview-domain-measure-value">
- {this.renderDebt('sqale_index', 'CODE_SMELL')}
- {this.props.renderRating('sqale_rating')}
- </div>
- <div className="overview-domain-measure-label">
- {getMetricName('effort')}
- {this.props.renderHistoryLink('sqale_index')}
- </div>
- </div>
- <div className="overview-domain-measure">
- <div className="overview-domain-measure-value">
- {this.props.renderIssues('code_smells', 'CODE_SMELL')}
- </div>
- <div className="overview-domain-measure-label offset-left">
- <CodeSmellIcon className="little-spacer-right " />
- {getMetricName('code_smells')}
- {this.props.renderHistoryLink('code_smells')}
- </div>
- </div>
- </div>
-
- {this.renderTimeline('before', true)}
- </div>
- );
- }
-
- render() {
- const { measures } = this.props;
- const codeSmellsMeasure = measures.find(measure => measure.metric.key === 'code_smells');
- if (codeSmellsMeasure == null) {
- return null;
- }
- return (
- <div className="overview-card" id="overview-code-smells">
- {this.renderHeader()}
-
- <div className="overview-domain-panel">
- {this.renderNutshell()}
- {this.renderLeak()}
- </div>
- </div>
- );
- }
-}
-
-export default enhance(CodeSmells);
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 { Link } from 'react-router';
+import enhance, { ComposedProps } from './enhance';
+import DateFromNow from '../../../components/intl/DateFromNow';
+import { getMetricName } from '../helpers/metrics';
+import { translate, translateWithParameters } from '../../../helpers/l10n';
+import { formatMeasure, isDiffMetric } from '../../../helpers/measures';
+import { getComponentIssuesUrl } from '../../../helpers/urls';
+import CodeSmellIcon from '../../../components/icons-components/CodeSmellIcon';
+
+export class CodeSmells extends React.PureComponent<ComposedProps> {
+ renderHeader() {
+ return this.props.renderHeader('Maintainability', translate('metric.code_smells.name'));
+ }
+
+ renderDebt(metric: string, type: string) {
+ const { branch, measures, component } = this.props;
+ const measure = measures.find(measure => measure.metric.key === metric);
+ const value = measure ? this.props.getValue(measure) : undefined;
+ const params = { branch, resolved: 'false', facetMode: 'effort', types: type };
+
+ if (isDiffMetric(metric)) {
+ Object.assign(params, { sinceLeakPeriod: 'true' });
+ }
+
+ return (
+ <Link to={getComponentIssuesUrl(component.key, params)}>
+ {formatMeasure(value, 'SHORT_WORK_DUR')}
+ </Link>
+ );
+ }
+
+ renderTimelineStartDate() {
+ if (!this.props.historyStartDate) {
+ return null;
+ }
+ return (
+ <DateFromNow date={this.props.historyStartDate}>
+ {fromNow => (
+ <span className="overview-domain-timeline-date">
+ {translateWithParameters('overview.started_x', fromNow)}
+ </span>
+ )}
+ </DateFromNow>
+ );
+ }
+
+ renderTimeline(range: string, displayDate?: boolean) {
+ return this.props.renderTimeline(
+ 'sqale_index',
+ range,
+ displayDate ? this.renderTimelineStartDate() : null
+ );
+ }
+
+ renderLeak() {
+ const { leakPeriod } = this.props;
+ if (!leakPeriod) {
+ return null;
+ }
+
+ return (
+ <div className="overview-domain-leak">
+ <div className="overview-domain-measures">
+ <div className="overview-domain-measure">
+ <div className="overview-domain-measure-value">
+ <span style={{ marginLeft: 30 }}>
+ {this.renderDebt('new_technical_debt', 'CODE_SMELL')}
+ </span>
+ {this.props.renderRating('new_maintainability_rating')}
+ </div>
+ <div className="overview-domain-measure-label">{getMetricName('new_effort')}</div>
+ </div>
+ <div className="overview-domain-measure">
+ <div className="overview-domain-measure-value">
+ {this.props.renderIssues('new_code_smells', 'CODE_SMELL')}
+ </div>
+ <div className="overview-domain-measure-label">
+ <CodeSmellIcon className="little-spacer-right" />
+ {getMetricName('new_code_smells')}
+ </div>
+ </div>
+ </div>
+
+ {this.renderTimeline('after')}
+ </div>
+ );
+ }
+
+ renderNutshell() {
+ return (
+ <div className="overview-domain-nutshell">
+ <div className="overview-domain-measures">
+ <div className="overview-domain-measure">
+ <div className="overview-domain-measure-value">
+ {this.renderDebt('sqale_index', 'CODE_SMELL')}
+ {this.props.renderRating('sqale_rating')}
+ </div>
+ <div className="overview-domain-measure-label">
+ {getMetricName('effort')}
+ {this.props.renderHistoryLink('sqale_index')}
+ </div>
+ </div>
+ <div className="overview-domain-measure">
+ <div className="overview-domain-measure-value">
+ {this.props.renderIssues('code_smells', 'CODE_SMELL')}
+ </div>
+ <div className="overview-domain-measure-label offset-left">
+ <CodeSmellIcon className="little-spacer-right " />
+ {getMetricName('code_smells')}
+ {this.props.renderHistoryLink('code_smells')}
+ </div>
+ </div>
+ </div>
+
+ {this.renderTimeline('before', true)}
+ </div>
+ );
+ }
+
+ render() {
+ const { measures } = this.props;
+ const codeSmellsMeasure = measures.find(measure => measure.metric.key === 'code_smells');
+ if (!codeSmellsMeasure) {
+ return null;
+ }
+ return (
+ <div className="overview-card" id="overview-code-smells">
+ {this.renderHeader()}
+
+ <div className="overview-domain-panel">
+ {this.renderNutshell()}
+ {this.renderLeak()}
+ </div>
+ </div>
+ );
+ }
+}
+
+export default enhance(CodeSmells);
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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 enhance from './enhance';
-import DrilldownLink from '../../../components/shared/DrilldownLink';
-import { getMetricName } from '../helpers/metrics';
-import { formatMeasure, getPeriodValue } from '../../../helpers/measures';
-import { translate } from '../../../helpers/l10n';
-import CoverageRating from '../../../components/ui/CoverageRating';
-
-class Coverage extends React.PureComponent {
- getCoverage() {
- const { measures } = this.props;
- const { value } = measures.find(measure => measure.metric.key === 'coverage');
- return Number(value);
- }
-
- getNewCoverageMeasure() {
- const { measures } = this.props;
- return measures.find(measure => measure.metric.key === 'new_coverage');
- }
-
- getNewLinesToCover() {
- const { measures } = this.props;
- return measures.find(measure => measure.metric.key === 'new_lines_to_cover');
- }
-
- renderHeader() {
- return this.props.renderHeader('Coverage', translate('metric.coverage.name'));
- }
-
- renderTimeline(range) {
- return this.props.renderTimeline('coverage', range);
- }
-
- renderTests() {
- return this.props.renderMeasure('tests');
- }
-
- renderCoverage() {
- const { branch, component } = this.props;
- const metric = 'coverage';
- const coverage = this.getCoverage();
-
- return (
- <div className="overview-domain-measure">
- <div className="display-inline-block text-middle big-spacer-right">
- <CoverageRating value={coverage} size="big" />
- </div>
-
- <div className="display-inline-block text-middle">
- <div className="overview-domain-measure-value">
- <DrilldownLink branch={branch} component={component.key} metric={metric}>
- <span className="js-overview-main-coverage">
- {formatMeasure(coverage, 'PERCENT')}
- </span>
- </DrilldownLink>
- </div>
-
- <div className="overview-domain-measure-label">
- {getMetricName('coverage')}
- {this.props.renderHistoryLink('coverage')}
- </div>
- </div>
- </div>
- );
- }
-
- renderNewCoverage() {
- const { branch, component, leakPeriod } = this.props;
- const newCoverageMeasure = this.getNewCoverageMeasure();
- const newLinesToCover = this.getNewLinesToCover();
-
- const newCoverageValue = newCoverageMeasure
- ? getPeriodValue(newCoverageMeasure, leakPeriod.index)
- : null;
- const newLinesToCoverValue = newLinesToCover
- ? getPeriodValue(newLinesToCover, leakPeriod.index)
- : null;
-
- const formattedValue =
- newCoverageValue != null ? (
- <div>
- <DrilldownLink
- branch={branch}
- component={component.key}
- metric={newCoverageMeasure.metric.key}>
- <span className="js-overview-main-new-coverage">
- {formatMeasure(newCoverageValue, 'PERCENT')}
- </span>
- </DrilldownLink>
- </div>
- ) : (
- <span>—</span>
- );
- const label =
- newLinesToCoverValue != null && newLinesToCoverValue > 0 ? (
- <div className="overview-domain-measure-label">
- {translate('overview.coverage_on')}
- <br />
- <DrilldownLink
- branch={branch}
- className="spacer-right overview-domain-secondary-measure-value"
- component={component.key}
- metric={newLinesToCover.metric.key}>
- <span className="js-overview-main-new-coverage">
- {formatMeasure(newLinesToCoverValue, 'SHORT_INT')}
- </span>
- </DrilldownLink>
- {getMetricName('new_lines_to_cover')}
- </div>
- ) : (
- <div className="overview-domain-measure-label">{getMetricName('new_coverage')}</div>
- );
- return (
- <div className="overview-domain-measure">
- <div className="overview-domain-measure-value">{formattedValue}</div>
- {label}
- </div>
- );
- }
-
- renderNutshell() {
- return (
- <div className="overview-domain-nutshell">
- <div className="overview-domain-measures">
- {this.renderCoverage()}
- {this.renderTests()}
- </div>
-
- {this.renderTimeline('before')}
- </div>
- );
- }
-
- renderLeak() {
- const { leakPeriod } = this.props;
- if (leakPeriod == null) {
- return null;
- }
- return (
- <div className="overview-domain-leak">
- <div className="overview-domain-measures">{this.renderNewCoverage()}</div>
-
- {this.renderTimeline('after')}
- </div>
- );
- }
-
- render() {
- const { measures } = this.props;
- const coverageMeasure = measures.find(measure => measure.metric.key === 'coverage');
- if (coverageMeasure == null) {
- return null;
- }
- return (
- <div className="overview-card">
- {this.renderHeader()}
-
- <div className="overview-domain-panel">
- {this.renderNutshell()}
- {this.renderLeak()}
- </div>
- </div>
- );
- }
-}
-
-export default enhance(Coverage);
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 enhance, { ComposedProps } from './enhance';
+import DrilldownLink from '../../../components/shared/DrilldownLink';
+import { getMetricName } from '../helpers/metrics';
+import { formatMeasure, getPeriodValue } from '../../../helpers/measures';
+import { translate } from '../../../helpers/l10n';
+import CoverageRating from '../../../components/ui/CoverageRating';
+
+export class Coverage extends React.PureComponent<ComposedProps> {
+ getCoverage() {
+ const measure = this.props.measures.find(measure => measure.metric.key === 'coverage');
+ return Number(measure ? measure.value : undefined);
+ }
+
+ renderHeader() {
+ return this.props.renderHeader('Coverage', translate('metric.coverage.name'));
+ }
+
+ renderTimeline(range: string) {
+ return this.props.renderTimeline('coverage', range);
+ }
+
+ renderTests() {
+ return this.props.renderMeasure('tests');
+ }
+
+ renderCoverage() {
+ const { branch, component } = this.props;
+ const metric = 'coverage';
+ const coverage = this.getCoverage();
+
+ return (
+ <div className="overview-domain-measure">
+ <div className="display-inline-block text-middle big-spacer-right">
+ <CoverageRating size="big" value={coverage} />
+ </div>
+
+ <div className="display-inline-block text-middle">
+ <div className="overview-domain-measure-value">
+ <DrilldownLink branch={branch} component={component.key} metric={metric}>
+ <span className="js-overview-main-coverage">
+ {formatMeasure(coverage, 'PERCENT')}
+ </span>
+ </DrilldownLink>
+ </div>
+
+ <div className="overview-domain-measure-label">
+ {getMetricName('coverage')}
+ {this.props.renderHistoryLink('coverage')}
+ </div>
+ </div>
+ </div>
+ );
+ }
+
+ renderNewCoverage() {
+ const { branch, component, leakPeriod, measures } = this.props;
+ if (!leakPeriod) {
+ return null;
+ }
+
+ const newCoverageMeasure = measures.find(measure => measure.metric.key === 'new_coverage');
+ const newCoverageValue =
+ newCoverageMeasure && getPeriodValue(newCoverageMeasure, leakPeriod.index);
+ const formattedValue =
+ newCoverageMeasure && newCoverageValue !== undefined ? (
+ <div>
+ <DrilldownLink
+ branch={branch}
+ component={component.key}
+ metric={newCoverageMeasure.metric.key}>
+ <span className="js-overview-main-new-coverage">
+ {formatMeasure(newCoverageValue, 'PERCENT')}
+ </span>
+ </DrilldownLink>
+ </div>
+ ) : (
+ <span>—</span>
+ );
+
+ const newLinesToCover = measures.find(measure => measure.metric.key === 'new_lines_to_cover');
+ const newLinesToCoverValue =
+ newLinesToCover && getPeriodValue(newLinesToCover, leakPeriod.index);
+ const label =
+ newLinesToCover && newLinesToCoverValue !== undefined && Number(newLinesToCoverValue) > 0 ? (
+ <div className="overview-domain-measure-label">
+ {translate('overview.coverage_on')}
+ <br />
+ <DrilldownLink
+ branch={branch}
+ className="spacer-right overview-domain-secondary-measure-value"
+ component={component.key}
+ metric={newLinesToCover.metric.key}>
+ <span className="js-overview-main-new-coverage">
+ {formatMeasure(newLinesToCoverValue, 'SHORT_INT')}
+ </span>
+ </DrilldownLink>
+ {getMetricName('new_lines_to_cover')}
+ </div>
+ ) : (
+ <div className="overview-domain-measure-label">{getMetricName('new_coverage')}</div>
+ );
+
+ return (
+ <div className="overview-domain-measure">
+ <div className="overview-domain-measure-value">{formattedValue}</div>
+ {label}
+ </div>
+ );
+ }
+
+ renderNutshell() {
+ return (
+ <div className="overview-domain-nutshell">
+ <div className="overview-domain-measures">
+ {this.renderCoverage()}
+ {this.renderTests()}
+ </div>
+
+ {this.renderTimeline('before')}
+ </div>
+ );
+ }
+
+ renderLeak() {
+ const { leakPeriod } = this.props;
+ if (!leakPeriod) {
+ return null;
+ }
+ return (
+ <div className="overview-domain-leak">
+ <div className="overview-domain-measures">{this.renderNewCoverage()}</div>
+
+ {this.renderTimeline('after')}
+ </div>
+ );
+ }
+
+ render() {
+ const { measures } = this.props;
+ const coverageMeasure = measures.find(measure => measure.metric.key === 'coverage');
+ if (!coverageMeasure) {
+ return null;
+ }
+ return (
+ <div className="overview-card">
+ {this.renderHeader()}
+
+ <div className="overview-domain-panel">
+ {this.renderNutshell()}
+ {this.renderLeak()}
+ </div>
+ </div>
+ );
+ }
+}
+
+export default enhance(Coverage);
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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 enhance from './enhance';
-import DrilldownLink from '../../../components/shared/DrilldownLink';
-import { getMetricName } from '../helpers/metrics';
-import { formatMeasure, getPeriodValue } from '../../../helpers/measures';
-import { translate } from '../../../helpers/l10n';
-import DuplicationsRating from '../../../components/ui/DuplicationsRating';
-
-class Duplications extends React.PureComponent {
- renderHeader() {
- return this.props.renderHeader('Duplications', translate('overview.domain.duplications'));
- }
-
- renderTimeline(range) {
- return this.props.renderTimeline('duplicated_lines_density', range);
- }
-
- renderDuplicatedBlocks() {
- return this.props.renderMeasure('duplicated_blocks');
- }
-
- renderDuplications() {
- const { branch, component, measures } = this.props;
- const measure = measures.find(measure => measure.metric.key === 'duplicated_lines_density');
- const duplications = Number(measure.value);
-
- return (
- <div className="overview-domain-measure">
- <div className="display-inline-block text-middle big-spacer-right">
- <DuplicationsRating value={duplications} size="big" />
- </div>
-
- <div className="display-inline-block text-middle">
- <div className="overview-domain-measure-value">
- <DrilldownLink
- branch={branch}
- component={component.key}
- metric="duplicated_lines_density">
- {formatMeasure(duplications, 'PERCENT')}
- </DrilldownLink>
- </div>
-
- <div className="overview-domain-measure-label offset-left">
- {getMetricName('duplications')}
- {this.props.renderHistoryLink('duplicated_lines_density')}
- </div>
- </div>
- </div>
- );
- }
-
- renderNewDuplications() {
- const { branch, component, measures, leakPeriod } = this.props;
- const newDuplicationsMeasure = measures.find(
- measure => measure.metric.key === 'new_duplicated_lines_density'
- );
- const newLinesMeasure = measures.find(measure => measure.metric.key === 'new_lines');
-
- const newDuplicationsValue = newDuplicationsMeasure
- ? getPeriodValue(newDuplicationsMeasure, leakPeriod.index)
- : null;
- const newLinesValue = newLinesMeasure
- ? getPeriodValue(newLinesMeasure, leakPeriod.index)
- : null;
-
- const formattedValue =
- newDuplicationsValue != null ? (
- <div>
- <DrilldownLink
- branch={branch}
- component={component.key}
- metric={newDuplicationsMeasure.metric.key}>
- <span className="js-overview-main-new-duplications">
- {formatMeasure(newDuplicationsValue, 'PERCENT')}
- </span>
- </DrilldownLink>
- </div>
- ) : (
- <span>—</span>
- );
- const label =
- newLinesValue != null && newLinesValue > 0 ? (
- <div className="overview-domain-measure-label">
- {translate('overview.duplications_on')}
- <br />
- <DrilldownLink
- branch={branch}
- className="spacer-right overview-domain-secondary-measure-value"
- component={component.key}
- metric={newLinesMeasure.metric.key}>
- <span className="js-overview-main-new-lines">
- {formatMeasure(newLinesValue, 'SHORT_INT')}
- </span>
- </DrilldownLink>
- {getMetricName('new_lines')}
- </div>
- ) : (
- <div className="overview-domain-measure-label">{getMetricName('new_duplications')}</div>
- );
- return (
- <div className="overview-domain-measure">
- <div className="overview-domain-measure-value">{formattedValue}</div>
- {label}
- </div>
- );
- }
-
- renderNutshell() {
- return (
- <div className="overview-domain-nutshell">
- <div className="overview-domain-measures">
- {this.renderDuplications()}
- {this.renderDuplicatedBlocks()}
- </div>
-
- {this.renderTimeline('before')}
- </div>
- );
- }
-
- renderLeak() {
- const { leakPeriod } = this.props;
- if (leakPeriod == null) {
- return null;
- }
- return (
- <div className="overview-domain-leak">
- <div className="overview-domain-measures">{this.renderNewDuplications()}</div>
-
- {this.renderTimeline('after')}
- </div>
- );
- }
-
- render() {
- const { measures } = this.props;
- const duplications = measures.find(
- measure => measure.metric.key === 'duplicated_lines_density'
- );
- if (duplications == null) {
- return null;
- }
- return (
- <div className="overview-card">
- {this.renderHeader()}
-
- <div className="overview-domain-panel">
- {this.renderNutshell()}
- {this.renderLeak()}
- </div>
- </div>
- );
- }
-}
-
-export default enhance(Duplications);
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 enhance, { ComposedProps } from './enhance';
+import DrilldownLink from '../../../components/shared/DrilldownLink';
+import { getMetricName } from '../helpers/metrics';
+import { formatMeasure, getPeriodValue } from '../../../helpers/measures';
+import { translate } from '../../../helpers/l10n';
+import DuplicationsRating from '../../../components/ui/DuplicationsRating';
+
+export class Duplications extends React.PureComponent<ComposedProps> {
+ renderHeader() {
+ return this.props.renderHeader('Duplications', translate('overview.domain.duplications'));
+ }
+
+ renderTimeline(range: string) {
+ return this.props.renderTimeline('duplicated_lines_density', range);
+ }
+
+ renderDuplicatedBlocks() {
+ return this.props.renderMeasure('duplicated_blocks');
+ }
+
+ renderDuplications() {
+ const { branch, component, measures } = this.props;
+
+ const measure = measures.find(measure => measure.metric.key === 'duplicated_lines_density');
+ if (!measure) {
+ return null;
+ }
+
+ const duplications = Number(measure.value);
+
+ return (
+ <div className="overview-domain-measure">
+ <div className="display-inline-block text-middle big-spacer-right">
+ <DuplicationsRating size="big" value={duplications} />
+ </div>
+
+ <div className="display-inline-block text-middle">
+ <div className="overview-domain-measure-value">
+ <DrilldownLink
+ branch={branch}
+ component={component.key}
+ metric="duplicated_lines_density">
+ {formatMeasure(duplications, 'PERCENT')}
+ </DrilldownLink>
+ </div>
+
+ <div className="overview-domain-measure-label offset-left">
+ {getMetricName('duplications')}
+ {this.props.renderHistoryLink('duplicated_lines_density')}
+ </div>
+ </div>
+ </div>
+ );
+ }
+
+ renderNewDuplications() {
+ const { branch, component, measures, leakPeriod } = this.props;
+ if (!leakPeriod) {
+ return null;
+ }
+
+ const newDuplicationsMeasure = measures.find(
+ measure => measure.metric.key === 'new_duplicated_lines_density'
+ );
+ const newDuplicationsValue =
+ newDuplicationsMeasure && getPeriodValue(newDuplicationsMeasure, leakPeriod.index);
+ const formattedValue =
+ newDuplicationsMeasure && newDuplicationsValue ? (
+ <div>
+ <DrilldownLink
+ branch={branch}
+ component={component.key}
+ metric={newDuplicationsMeasure.metric.key}>
+ <span className="js-overview-main-new-duplications">
+ {formatMeasure(newDuplicationsValue, 'PERCENT')}
+ </span>
+ </DrilldownLink>
+ </div>
+ ) : (
+ <span>—</span>
+ );
+
+ const newLinesMeasure = measures.find(measure => measure.metric.key === 'new_lines');
+ const newLinesValue = newLinesMeasure && getPeriodValue(newLinesMeasure, leakPeriod.index);
+ const label =
+ newLinesMeasure && newLinesValue !== undefined && Number(newLinesValue) > 0 ? (
+ <div className="overview-domain-measure-label">
+ {translate('overview.duplications_on')}
+ <br />
+ <DrilldownLink
+ branch={branch}
+ className="spacer-right overview-domain-secondary-measure-value"
+ component={component.key}
+ metric={newLinesMeasure.metric.key}>
+ <span className="js-overview-main-new-lines">
+ {formatMeasure(newLinesValue, 'SHORT_INT')}
+ </span>
+ </DrilldownLink>
+ {getMetricName('new_lines')}
+ </div>
+ ) : (
+ <div className="overview-domain-measure-label">{getMetricName('new_duplications')}</div>
+ );
+
+ return (
+ <div className="overview-domain-measure">
+ <div className="overview-domain-measure-value">{formattedValue}</div>
+ {label}
+ </div>
+ );
+ }
+
+ renderNutshell() {
+ return (
+ <div className="overview-domain-nutshell">
+ <div className="overview-domain-measures">
+ {this.renderDuplications()}
+ {this.renderDuplicatedBlocks()}
+ </div>
+
+ {this.renderTimeline('before')}
+ </div>
+ );
+ }
+
+ renderLeak() {
+ const { leakPeriod } = this.props;
+ if (leakPeriod == null) {
+ return null;
+ }
+ return (
+ <div className="overview-domain-leak">
+ <div className="overview-domain-measures">{this.renderNewDuplications()}</div>
+
+ {this.renderTimeline('after')}
+ </div>
+ );
+ }
+
+ render() {
+ const { measures } = this.props;
+ const duplications = measures.find(
+ measure => measure.metric.key === 'duplicated_lines_density'
+ );
+ if (duplications == null) {
+ return null;
+ }
+ return (
+ <div className="overview-card">
+ {this.renderHeader()}
+
+ <div className="overview-domain-panel">
+ {this.renderNutshell()}
+ {this.renderLeak()}
+ </div>
+ </div>
+ );
+ }
+}
+
+export default enhance(Duplications);
import { Link } from 'react-router';
import DrilldownLink from '../../../components/shared/DrilldownLink';
import BubblesIcon from '../../../components/icons-components/BubblesIcon';
-import DateTimeFormatter from '../../../components/intl/DateTimeFormatter';
import HistoryIcon from '../../../components/icons-components/HistoryIcon';
import Rating from '../../../components/ui/Rating';
import Timeline from '../components/Timeline';
getRatingTooltip,
MeasureEnhanced
} from '../../../helpers/measures';
-import { translateWithParameters, getLocalizedMetricName } from '../../../helpers/l10n';
+import { getLocalizedMetricName } from '../../../helpers/l10n';
import { getPeriodDate } from '../../../helpers/periods';
import {
getComponentDrilldownUrl,
Object.assign(params, { sinceLeakPeriod: 'true' });
}
- const tooltip = component.analysisDate && (
- <DateTimeFormatter date={component.analysisDate}>
- {formattedAnalysisDate => (
- <span>
- {translateWithParameters('widget.as_calculated_on_x', formattedAnalysisDate)}
- </span>
- )}
- </DateTimeFormatter>
- );
-
return (
- <Tooltip overlay={tooltip} placement="top">
- <Link to={getComponentIssuesUrl(component.key, params)}>
- {formatMeasure(value, 'SHORT_INT')}
- </Link>
- </Tooltip>
+ <Link to={getComponentIssuesUrl(component.key, params)}>
+ {formatMeasure(value, 'SHORT_INT')}
+ </Link>
);
};
getValue={this.getValue}
renderHeader={this.renderHeader}
renderHistoryLink={this.renderHistoryLink}
+ renderIssues={this.renderIssues}
renderMeasure={this.renderMeasure}
renderRating={this.renderRating}
- renderIssues={this.renderIssues}
renderTimeline={this.renderTimeline}
/>
);
overview.badges.quality_gate.alt=SonarCloud Quality Gate badge
overview.badges.quality_gate.description=This badge dynamically displays the current quality gate status of your project.
-widget.as_calculated_on_x=As calculated on {0}
-
#------------------------------------------------------------------------------
#