);
}
- renderComponentMeasuresLink() {
+ renderComponentMeasuresOldLink() {
return (
<li>
<Link
- to={{ pathname: '/component_measures', query: { id: this.props.component.key } }}
+ to={{ pathname: '/component_measures_old', query: { id: this.props.component.key } }}
activeClassName="active">
- {translate('layout.measures')}
+ Old Measures
</Link>
</li>
);
<NavBarTabs>
{this.renderDashboardLink()}
{this.renderIssuesLink()}
- {this.renderComponentMeasuresLink()}
+ {this.renderComponentMeasuresOldLink()}
{this.renderCodeLink()}
{this.renderActivityLink()}
{this.renderAdministration()}
style={Object {}}
to={
Object {
- "pathname": "/component_measures",
+ "pathname": "/component_measures_old",
"query": Object {
"id": "foo",
},
style={Object {}}
to={
Object {
- "pathname": "/component_measures",
+ "pathname": "/component_measures_old",
"query": Object {
"id": "foo",
},
import codeRoutes from '../../apps/code/routes';
import codingRulesRoutes from '../../apps/coding-rules/routes';
import componentRoutes from '../../apps/component/routes';
-import componentMeasuresRoutes from '../../apps/component-measures/routes';
+import componentMeasuresRoutes from '../../apps/component-measures-old/routes';
import customMeasuresRoutes from '../../apps/custom-measures/routes';
import groupsRoutes from '../../apps/groups/routes';
import issuesRoutes from '../../apps/issues/routes';
getComponent={() =>
import('../components/ProjectContainer').then(i => i.default)}>
<Route path="code" childRoutes={codeRoutes} />
- <Route path="component_measures" childRoutes={componentMeasuresRoutes} />
+ <Route path="component_measures_old" childRoutes={componentMeasuresRoutes} />
<Route path="custom_measures" childRoutes={customMeasuresRoutes} />
<Route path="dashboard" childRoutes={overviewRoutes} />
<Route path="project">
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import React from 'react';
-import Measure from '../../component-measures/components/Measure';
+import Measure from '../../component-measures-old/components/Measure';
const ComponentMeasure = ({ component, metricKey, metricType }) => {
const isProject = component.qualifier === 'TRK';
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 Helmet from 'react-helmet';
+import Spinner from './../components/Spinner';
+import { translate } from '../../../helpers/l10n';
+import '../styles.css';
+
+export default class App extends React.PureComponent {
+ state = { componentSet: false };
+
+ componentDidMount() {
+ this.props.setComponent(this.props.component);
+ this.props.fetchMetrics();
+ this.setState({ componentSet: true });
+ }
+
+ render() {
+ if (this.props.metrics == null || !this.state.componentSet) {
+ return <Spinner />;
+ }
+
+ return (
+ <main id="component-measures">
+ <Helmet title={translate('layout.measures')} />
+ {this.props.children}
+ </main>
+ );
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 { connect } from 'react-redux';
+import App from './App';
+import { fetchMetrics, setComponent } from './actions';
+import { getComponent, getMeasuresAppAllMetrics } from '../../../store/rootReducer';
+
+const mapStateToProps = (state, ownProps) => ({
+ component: getComponent(state, ownProps.location.query.id),
+ metrics: getMeasuresAppAllMetrics(state)
+});
+
+const mapDispatchToProps = dispatch => {
+ return {
+ fetchMetrics: () => dispatch(fetchMetrics()),
+ setComponent: component => dispatch(setComponent(component))
+ };
+};
+
+export default connect(mapStateToProps, mapDispatchToProps)(App);
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 { getMetrics } from '../../../api/metrics';
+
+/*
+ * Actions
+ */
+
+export const DISPLAY_HOME = 'measuresApp/app/DISPLAY_HOME';
+export const RECEIVE_METRICS = 'measuresApp/app/RECEIVE_METRICS';
+export const SET_COMPONENT = 'measuresApp/app/SET_COMPONENT';
+
+/*
+ * Action Creators
+ */
+
+export function displayHome() {
+ return { type: DISPLAY_HOME };
+}
+
+function receiveMetrics(metrics) {
+ return { type: RECEIVE_METRICS, metrics };
+}
+
+export function setComponent(component) {
+ return { type: SET_COMPONENT, component };
+}
+
+/*
+ * Workflow
+ */
+
+export function fetchMetrics() {
+ return dispatch => {
+ getMetrics().then(metrics => {
+ dispatch(receiveMetrics(metrics));
+ });
+ };
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 { RECEIVE_METRICS, SET_COMPONENT } from './actions';
+
+const initialState = {
+ metrics: undefined
+};
+
+export default function appReducer(state = initialState, action = {}) {
+ switch (action.type) {
+ case RECEIVE_METRICS:
+ return { ...state, metrics: action.metrics };
+ case SET_COMPONENT:
+ return { ...state, component: action.component };
+ default:
+ return state;
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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';
+
+export default function IconBubbles() {
+ /* eslint max-len: 0 */
+ return (
+ <svg
+ className="measure-tab-icon"
+ viewBox="0 0 512 448"
+ fillRule="evenodd"
+ clipRule="evenodd"
+ strokeLinejoin="round"
+ strokeMiterlimit="1.414">
+ <path d="M352 256c52.984 0 96 43.016 96 96s-43.016 96-96 96-96-43.016-96-96 43.016-96 96-96zM128 96c70.645 0 128 57.355 128 128 0 70.645-57.355 128-128 128C57.355 352 0 294.645 0 224 0 153.355 57.355 96 128 96zM352 0c52.984 0 96 43.016 96 96s-43.016 96-96 96-96-43.016-96-96 43.016-96 96-96z" />
+ </svg>
+ );
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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';
+
+export default function ListIcon() {
+ /* eslint max-len: 0 */
+ return (
+ <svg
+ className="measure-tab-icon"
+ viewBox="0 0 448 448"
+ fillRule="evenodd"
+ clipRule="evenodd"
+ strokeLinejoin="round"
+ strokeMiterlimit="1.414">
+ <path d="M448 48c0-8.83-7.17-16-16-16H16C7.17 32 0 39.17 0 48v32c0 8.83 7.17 16 16 16h416c8.83 0 16-7.17 16-16V48zM448 144c0-8.83-7.17-16-16-16H16c-8.83 0-16 7.17-16 16v32c0 8.83 7.17 16 16 16h416c8.83 0 16-7.17 16-16v-32zM448 240c0-8.83-7.17-16-16-16H16c-8.83 0-16 7.17-16 16v32c0 8.83 7.17 16 16 16h416c8.83 0 16-7.17 16-16v-32zM448 336.03c0-8.83-7.17-16-16-16H16c-8.83 0-16 7.17-16 16v32c0 8.83 7.17 16 16 16h416c8.83 0 16-7.17 16-16v-32z" />
+ </svg>
+ );
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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';
+
+export default function IconTree() {
+ /* eslint max-len: 0 */
+ return (
+ <svg
+ className="measure-tab-icon"
+ viewBox="0 0 448 448"
+ fillRule="evenodd"
+ clipRule="evenodd"
+ strokeLinejoin="round"
+ strokeMiterlimit="1.414">
+ <path d="M448 48c0-8.83-7.17-16-16-16H16C7.17 32 0 39.17 0 48v32c0 8.83 7.17 16 16 16h416c8.83 0 16-7.17 16-16V48zM448 144c0-8.83-6.146-16-13.714-16H77.714C70.144 128 64 135.17 64 144v32c0 8.83 6.145 16 13.714 16h356.572c7.568 0 13.714-7.17 13.714-16v-32zM448 240c0-8.83-5.12-16-11.428-16H139.428C133.12 224 128 231.17 128 240v32c0 8.83 5.12 16 11.428 16h297.144c6.307 0 11.428-7.17 11.428-16v-32zM448 336.03c0-8.83-4.097-16-9.142-16H201.143c-5.046 0-9.143 7.17-9.143 16v32c0 8.83 4.097 16 9.143 16h237.715c5.045 0 9.142-7.17 9.142-16v-32z" />
+ </svg>
+ );
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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';
+
+export default function IconTreemap() {
+ return (
+ <svg
+ className="measure-tab-icon"
+ viewBox="0 0 448 448"
+ fillRule="evenodd"
+ clipRule="evenodd"
+ strokeLinejoin="round"
+ strokeMiterlimit="1.414">
+ <path d="M0 0h224v448H0zM256 0h192v256H256zM256 288h192v160H256z" />
+ </svg>
+ );
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 moment from 'moment';
+import Tooltip from '../../../components/controls/Tooltip';
+import { getPeriodLabel, getPeriodDate } from '../../../helpers/periods';
+import { translate, translateWithParameters } from '../../../helpers/l10n';
+
+export default function LeakPeriodLegend({ component, period }) {
+ if (component.qualifier === 'APP') {
+ return (
+ <div className="measures-domains-leak-header">
+ {translate('issues.leak_period')}
+ </div>
+ );
+ }
+
+ const label = (
+ <div className="measures-domains-leak-header">
+ {translateWithParameters('overview.leak_period_x', getPeriodLabel(period))}
+ </div>
+ );
+
+ if (period.mode === 'days') {
+ return label;
+ }
+
+ const date = getPeriodDate(period);
+ const fromNow = moment(date).fromNow();
+ const tooltip = fromNow + ', ' + moment(date).format('LL');
+ return (
+ <Tooltip placement="bottom" overlay={tooltip}>
+ {label}
+ </Tooltip>
+ );
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 PropTypes from 'prop-types';
+import Rating from '../../../components/ui/Rating';
+import Level from '../../../components/ui/Level';
+import { formatMeasure, isDiffMetric } from '../../../helpers/measures';
+import { TooltipsContainer } from '../../../components/mixins/tooltips-mixin';
+import { formatLeak, getRatingTooltip } from '../utils';
+
+export default class Measure extends React.PureComponent {
+ static propTypes = {
+ className: PropTypes.string,
+ measure: PropTypes.object,
+ metric: PropTypes.object,
+ decimals: PropTypes.number
+ };
+
+ renderRating(measure, metric) {
+ const value = isDiffMetric(metric.key) ? measure.leak : measure.value;
+ const tooltip = getRatingTooltip(metric.key, value);
+ const rating = <Rating value={value} />;
+
+ if (tooltip) {
+ return (
+ <TooltipsContainer>
+ <span>
+ <span title={tooltip} data-toggle="tooltip">
+ {rating}
+ </span>
+ </span>
+ </TooltipsContainer>
+ );
+ }
+
+ return rating;
+ }
+
+ render() {
+ const { measure, metric, decimals, className } = this.props;
+ const finalMetric = metric || measure.metric;
+
+ if (finalMetric.type === 'RATING') {
+ return this.renderRating(measure, finalMetric);
+ }
+
+ if (finalMetric.type === 'LEVEL') {
+ return <Level level={measure.value} />;
+ }
+
+ const formattedValue = isDiffMetric(finalMetric.key)
+ ? formatLeak(measure.leak, finalMetric, { decimals })
+ : formatMeasure(measure.value, finalMetric.type, { decimals });
+ return (
+ <span className={className}>
+ {formattedValue != null ? formattedValue : '–'}
+ </span>
+ );
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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';
+
+export default function Spinner() {
+ return <i className="spinner spinner-margin" />;
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 Spinner from './../Spinner';
+import OriginalBubbleChart from '../../../../components/charts/BubbleChart';
+import bubbles from '../../config/bubbles';
+import { getComponentLeaves } from '../../../../api/components';
+import { formatMeasure } from '../../../../helpers/measures';
+import Workspace from '../../../../components/workspace/main';
+import { getComponentUrl } from '../../../../helpers/urls';
+import { getLocalizedMetricName, translateWithParameters } from '../../../../helpers/l10n';
+
+const HEIGHT = 500;
+const BUBBLES_LIMIT = 500;
+
+function getMeasure(component, metric) {
+ return Number(component.measures[metric]) || 0;
+}
+
+export default class BubbleChart extends React.PureComponent {
+ state = {
+ fetching: 0,
+ files: []
+ };
+
+ componentWillMount() {
+ this.updateMetrics(this.props);
+ }
+
+ componentDidMount() {
+ this.mounted = true;
+ this.fetchFiles();
+ }
+
+ componentWillUpdate(nextProps) {
+ this.updateMetrics(nextProps);
+ }
+
+ componentDidUpdate(nextProps) {
+ if (nextProps.domainName !== this.props.domainName) {
+ this.fetchFiles();
+ }
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
+ updateMetrics(props) {
+ const { metrics, domainName } = props;
+ const conf = bubbles[domainName];
+ this.xMetric = metrics.find(m => m.key === conf.x);
+ this.yMetric = metrics.find(m => m.key === conf.y);
+ this.sizeMetric = metrics.find(m => m.key === conf.size);
+ }
+
+ fetchFiles() {
+ const { component } = this.props;
+ const metrics = [this.xMetric.key, this.yMetric.key, this.sizeMetric.key];
+ const options = {
+ s: 'metric',
+ metricSort: this.sizeMetric.key,
+ asc: false,
+ ps: BUBBLES_LIMIT
+ };
+
+ if (this.mounted) {
+ this.setState({ fetching: this.state.fetching + 1 });
+ }
+
+ getComponentLeaves(component.key, metrics, options).then(r => {
+ const files = r.components.map(file => {
+ const measures = {};
+
+ file.measures.forEach(measure => {
+ measures[measure.metric] = measure.value;
+ });
+ return { ...file, measures };
+ });
+
+ if (this.mounted) {
+ this.setState({
+ files,
+ fetching: this.state.fetching - 1,
+ total: files.length
+ });
+ }
+ });
+ }
+
+ getTooltip(component) {
+ const x = formatMeasure(getMeasure(component, this.xMetric.key), this.xMetric.type);
+ const y = formatMeasure(getMeasure(component, this.yMetric.key), this.yMetric.type);
+ const size = formatMeasure(getMeasure(component, this.sizeMetric.key), this.sizeMetric.type);
+ const inner = [
+ component.name,
+ `${this.xMetric.name}: ${x}`,
+ `${this.yMetric.name}: ${y}`,
+ `${this.sizeMetric.name}: ${size}`
+ ].join('<br>');
+
+ return `<div class="text-left">${inner}</div>`;
+ }
+
+ handleBubbleClick(component) {
+ if (['FIL', 'UTS'].includes(component.qualifier)) {
+ Workspace.openComponent({ key: component.key });
+ } else {
+ window.location = getComponentUrl(component.refKey || component.key);
+ }
+ }
+
+ renderBubbleChart() {
+ const items = this.state.files.map(file => {
+ return {
+ x: getMeasure(file, this.xMetric.key),
+ y: getMeasure(file, this.yMetric.key),
+ size: getMeasure(file, this.sizeMetric.key),
+ link: file,
+ tooltip: this.getTooltip(file)
+ };
+ });
+
+ const formatXTick = tick => formatMeasure(tick, this.xMetric.type);
+ const formatYTick = tick => formatMeasure(tick, this.yMetric.type);
+
+ return (
+ <OriginalBubbleChart
+ items={items}
+ height={HEIGHT}
+ padding={[25, 60, 50, 60]}
+ formatXTick={formatXTick}
+ formatYTick={formatYTick}
+ onBubbleClick={this.handleBubbleClick.bind(this)}
+ />
+ );
+ }
+
+ render() {
+ const { fetching } = this.state;
+
+ if (fetching) {
+ return (
+ <div className="measure-details-bubble-chart">
+ <div className="note text-center" style={{ lineHeight: `${HEIGHT}px` }}>
+ <Spinner />
+ </div>
+ </div>
+ );
+ }
+
+ return (
+ <div className="measure-details-bubble-chart">
+ <div>
+ {this.renderBubbleChart()}
+ </div>
+
+ <div className="measure-details-bubble-chart-axis x">
+ {getLocalizedMetricName(this.xMetric)}
+ </div>
+ <div className="measure-details-bubble-chart-axis y">
+ {getLocalizedMetricName(this.yMetric)}
+ </div>
+ <div className="measure-details-bubble-chart-axis size">
+ {translateWithParameters(
+ 'component_measures.legend.size_x',
+ getLocalizedMetricName(this.sizeMetric)
+ )}
+ </div>
+ </div>
+ );
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 { connect } from 'react-redux';
+import MeasureBubbleChart from './BubbleChart';
+import { getMeasuresAppAllMetrics, getMeasuresAppComponent } from '../../../../store/rootReducer';
+
+const mapStateToProps = state => {
+ return {
+ component: getMeasuresAppComponent(state),
+ metrics: getMeasuresAppAllMetrics(state)
+ };
+};
+
+const mapDispatchToProps = () => {
+ return {};
+};
+
+export default connect(mapStateToProps, mapDispatchToProps)(MeasureBubbleChart);
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.
+ */
+const bubblesConfig = {
+ Reliability: { x: 'ncloc', y: 'reliability_remediation_effort', size: 'bugs' },
+ Security: { x: 'ncloc', y: 'security_remediation_effort', size: 'vulnerabilities' },
+ Maintainability: { x: 'ncloc', y: 'sqale_index', size: 'code_smells' },
+ Coverage: { x: 'complexity', y: 'coverage', size: 'uncovered_lines' },
+ Duplications: { x: 'ncloc', y: 'duplicated_lines', size: 'duplicated_blocks' }
+};
+
+export default bubblesConfig;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.
+ */
+export default {
+ coverage: ['uncovered_lines', 'uncovered_conditions'],
+ line_coverage: ['uncovered_lines'],
+ branch_coverage: ['uncovered_conditions'],
+ uncovered_lines: ['line_coverage'],
+ uncovered_conditions: ['branch_coverage'],
+
+ new_coverage: ['new_uncovered_lines', 'new_uncovered_conditions'],
+ new_line_coverage: ['new_uncovered_lines'],
+ new_branch_coverage: ['new_uncovered_conditions'],
+ new_uncovered_lines: ['new_line_coverage'],
+ new_uncovered_conditions: ['new_branch_coverage'],
+
+ duplicated_lines_density: ['duplicated_lines'],
+ new_duplicated_lines_density: ['new_duplicated_lines'],
+ duplicated_lines: ['duplicated_lines_density'],
+ new_duplicated_lines: ['new_duplicated_lines_density']
+};
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.
+ */
+export const domains = {
+ Reliability: {
+ main: ['bugs', 'new_bugs', 'reliability_rating'],
+ order: [
+ 'bugs',
+ 'new_bugs',
+ 'reliability_rating',
+ 'reliability_remediation_effort',
+ 'new_reliability_remediation_effort'
+ ]
+ },
+
+ Security: {
+ main: ['vulnerabilities', 'new_vulnerabilities', 'security_rating'],
+ order: [
+ 'vulnerabilities',
+ 'new_vulnerabilities',
+ 'security_rating',
+ 'security_remediation_effort',
+ 'new_security_remediation_effort'
+ ]
+ },
+
+ Maintainability: {
+ main: ['code_smells', 'new_code_smells', 'sqale_rating'],
+ order: [
+ 'code_smells',
+ 'new_code_smells',
+ 'sqale_rating',
+ 'sqale_index',
+ 'new_technical_debt',
+ 'sqale_debt_ratio',
+ 'new_sqale_debt_ratio',
+ 'effort_to_reach_maintainability_rating_a'
+ ]
+ },
+
+ Coverage: {
+ main: ['coverage', 'new_coverage', 'tests'],
+ order: [
+ 'coverage',
+ 'new_coverage',
+ 'line_coverage',
+ 'new_line_coverage',
+ 'branch_coverage',
+ 'new_branch_coverage',
+ 'uncovered_lines',
+ 'new_uncovered_lines',
+ 'uncovered_conditions',
+ 'new_uncovered_conditions',
+ 'new_lines_to_cover',
+
+ 'lines_to_cover',
+
+ 'tests',
+ 'test_success',
+ 'test_errors',
+ 'test_failures',
+ 'skipped_tests',
+ 'test_success_density',
+ 'test_execution_time'
+ ]
+ },
+
+ Duplications: {
+ main: ['duplicated_lines_density', 'new_duplicated_lines_density'],
+ order: [
+ 'duplicated_lines_density',
+ 'new_duplicated_lines_density',
+ 'duplicated_blocks',
+ 'new_duplicated_blocks',
+ 'duplicated_lines',
+ 'new_duplicated_lines',
+ 'duplicated_files'
+ ]
+ },
+
+ Size: {
+ main: ['ncloc'],
+ order: [
+ 'ncloc',
+ 'lines',
+ 'new_lines',
+ 'statements',
+ 'functions',
+ 'classes',
+ 'files',
+ 'directories'
+ ]
+ },
+
+ Complexity: {
+ main: ['complexity'],
+ order: ['complexity', 'function_complexity', 'file_complexity', 'class_complexity']
+ },
+
+ Releasability: {
+ main: ['alert_status', 'releasability_rating'],
+ order: ['alert_status']
+ },
+
+ Issues: {
+ main: ['violations', 'new_violations'],
+ order: [
+ 'violations',
+ 'new_violations',
+ 'blocker_violations',
+ 'new_blocker_violations',
+ 'critical_violations',
+ 'new_critical_violations',
+ 'major_violations',
+ 'new_major_violations',
+ 'minor_violations',
+ 'new_minor_violations',
+ 'info_violations',
+ 'new_info_violations',
+ 'open_issues',
+ 'reopened_issues',
+ 'confirmed_issues',
+ 'false_positive_issues'
+ ]
+ }
+};
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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, IndexLink } from 'react-router';
+import Spinner from './../components/Spinner';
+import MeasureDetailsHeader from './MeasureDetailsHeader';
+import MeasureDrilldown from './drilldown/MeasureDrilldown';
+import MetricNotFound from './MetricNotFound';
+import { getPeriod, getPeriodDate } from '../../../helpers/periods';
+import { translate, translateWithParameters } from '../../../helpers/l10n';
+
+export default class MeasureDetails extends React.PureComponent {
+ mounted: boolean;
+
+ state = {
+ loading: true
+ };
+
+ componentDidMount() {
+ this.mounted = true;
+ this.loadData();
+ }
+
+ componentDidUpdate(prevProps) {
+ if (prevProps.params.metricKey !== this.props.params.metricKey) {
+ this.loadData();
+ }
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
+ metricExists(): boolean {
+ const { metrics } = this.props;
+ const { metricKey } = this.props.params;
+ const metric = metrics.find(metric => metric.key === metricKey);
+ return !!metric;
+ }
+
+ loadData() {
+ if (this.metricExists()) {
+ this.setState({ loading: true });
+ const periodIndex = this.props.location.query.period || 1;
+ const onLoaded = () => this.mounted && this.setState({ loading: false });
+ this.props
+ .fetchMeasure(this.props.params.metricKey, Number(periodIndex))
+ .then(onLoaded, onLoaded);
+ }
+ }
+
+ render() {
+ if (!this.metricExists()) {
+ return <MetricNotFound />;
+ }
+
+ if (this.state.loading) {
+ return <Spinner />;
+ }
+
+ const { component, metric, secondaryMeasure, measure, periods, children } = this.props;
+
+ if (!measure) {
+ return <MetricNotFound />;
+ }
+
+ const { tab } = this.props.params;
+ const periodIndex = this.props.location.query.period || 1;
+ const period = getPeriod(periods, Number(periodIndex));
+ const periodDate = getPeriodDate(period);
+
+ return (
+ <section id="component-measures-details" className="page page-container page-limited">
+ <div className="note">
+ <IndexLink
+ to={{ pathname: '/component_measures_old', query: { id: component.key } }}
+ id="component-measures-back-to-all-measures"
+ className="text-muted">
+ {translate('component_measures.all_measures')}
+ </IndexLink>
+ {!!metric.domain &&
+ <span>
+ {' / '}
+ <Link
+ to={{
+ pathname: `/component_measures_old/domain/${metric.domain}`,
+ query: { id: component.key }
+ }}
+ className="text-muted">
+ {translateWithParameters('component_measures.domain_measures', metric.domain)}
+ </Link>
+ </span>}
+ </div>
+
+ <MeasureDetailsHeader
+ component={component}
+ leakPeriod={period}
+ measure={measure}
+ metric={metric}
+ secondaryMeasure={secondaryMeasure}
+ />
+
+ {measure &&
+ <MeasureDrilldown
+ component={component}
+ metric={metric}
+ tab={tab}
+ leakPeriod={period}
+ leakPeriodDate={periodDate}>
+ {children}
+ </MeasureDrilldown>}
+ </section>
+ );
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 { connect } from 'react-redux';
+import MeasureDetails from './MeasureDetails';
+import { fetchMeasure } from './actions';
+import {
+ getMeasuresAppAllMetrics,
+ getMeasuresAppDetailsMetric,
+ getMeasuresAppDetailsMeasure,
+ getMeasuresAppDetailsSecondaryMeasure,
+ getMeasuresAppDetailsPeriods,
+ getMeasuresAppComponent
+} from '../../../store/rootReducer';
+
+const mapStateToProps = state => {
+ return {
+ component: getMeasuresAppComponent(state),
+ metrics: getMeasuresAppAllMetrics(state),
+ metric: getMeasuresAppDetailsMetric(state),
+ measure: getMeasuresAppDetailsMeasure(state),
+ secondaryMeasure: getMeasuresAppDetailsSecondaryMeasure(state),
+ periods: getMeasuresAppDetailsPeriods(state)
+ };
+};
+
+const mapDispatchToProps = { fetchMeasure };
+
+export default connect(mapStateToProps, mapDispatchToProps)(MeasureDetails);
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 Measure from './../components/Measure';
+import LanguageDistribution from '../../../components/charts/LanguageDistribution';
+import LeakPeriodLegend from '../components/LeakPeriodLegend';
+import IssueTypeIcon from '../../../components/ui/IssueTypeIcon';
+import HistoryIcon from '../../../components/icons-components/HistoryIcon';
+import Tooltip from '../../../components/controls/Tooltip';
+import { ComplexityDistribution } from '../../../components/shared/complexity-distribution';
+import { isDiffMetric } from '../../../helpers/measures';
+import { TooltipsContainer } from '../../../components/mixins/tooltips-mixin';
+import { getComponentMeasureHistory } from '../../../helpers/urls';
+import { getLocalizedMetricName, translate } from '../../../helpers/l10n';
+
+export default function MeasureDetailsHeader({
+ component,
+ measure,
+ metric,
+ secondaryMeasure,
+ leakPeriod
+}) {
+ const isDiff = isDiffMetric(metric.key);
+ return (
+ <header className="measure-details-header">
+ <h2 className="measure-details-metric">
+ <IssueTypeIcon query={metric.key} className="little-spacer-right" />
+ {getLocalizedMetricName(metric)}
+ {!isDiff &&
+ <Tooltip placement="right" overlay={translate('component_measures.show_metric_history')}>
+ <Link
+ className="js-show-history spacer-left button button-small button-compact"
+ to={getComponentMeasureHistory(component.key, metric.key)}>
+ <HistoryIcon />
+ </Link>
+ </Tooltip>}
+ </h2>
+
+ {isDiff &&
+ <div className="pull-right">
+ <LeakPeriodLegend component={component} period={leakPeriod} />
+ </div>}
+
+ <TooltipsContainer options={{ html: false }}>
+ <div className="measure-details-value">
+ {isDiff
+ ? <div className="measure-details-value-leak">
+ <Measure measure={measure} metric={metric} />
+ </div>
+ : <div className="measure-details-value-absolute">
+ <Measure measure={measure} metric={metric} />
+ </div>}
+
+ {secondaryMeasure &&
+ secondaryMeasure.metric === 'ncloc_language_distribution' &&
+ <div className="measure-details-secondary">
+ <LanguageDistribution distribution={secondaryMeasure.value} />
+ </div>}
+
+ {secondaryMeasure &&
+ secondaryMeasure.metric === 'function_complexity_distribution' &&
+ <div className="measure-details-secondary">
+ <ComplexityDistribution distribution={secondaryMeasure.value} of="function" />
+ </div>}
+
+ {secondaryMeasure &&
+ secondaryMeasure.metric === 'file_complexity_distribution' &&
+ <div className="measure-details-secondary">
+ <ComplexityDistribution distribution={secondaryMeasure.value} of="file" />
+ </div>}
+ </div>
+ </TooltipsContainer>
+ </header>
+ );
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 { translate } from '../../../helpers/l10n';
+
+export default function MetricNotFound() {
+ return (
+ <div className="page page-limited">
+ <div className="alert alert-danger">
+ {translate('component_measures.not_found')}
+ </div>
+ </div>
+ );
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 { getMeasuresAndMeta } from '../../../api/measures';
+import { enhanceWithLeak } from '../utils';
+import { getMeasuresAppComponent, getMeasuresAppAllMetrics } from '../../../store/rootReducer';
+
+/*
+ * Actions
+ */
+
+export const REQUEST_MEASURE = 'measuresApp/details/REQUEST_MEASURE';
+export const RECEIVE_MEASURE = 'measuresApp/details/RECEIVE_MEASURE';
+
+/*
+ * Action Creators
+ */
+
+function requestMeasure(metric) {
+ return { type: REQUEST_MEASURE, metric };
+}
+
+function receiveMeasure(measure, secondaryMeasure, periods) {
+ return { type: RECEIVE_MEASURE, measure, secondaryMeasure, periods };
+}
+
+/*
+ * Workflow
+ */
+
+export function fetchMeasure(metricKey, periodIndex = 1) {
+ return (dispatch, getState) => {
+ const state = getState();
+ const component = getMeasuresAppComponent(state);
+ const metrics = getMeasuresAppAllMetrics(state);
+ const metricsToRequest = [metricKey];
+
+ if (metricKey === 'ncloc') {
+ metricsToRequest.push('ncloc_language_distribution');
+ }
+ if (metricKey === 'function_complexity') {
+ metricsToRequest.push('function_complexity_distribution');
+ }
+ if (metricKey === 'file_complexity') {
+ metricsToRequest.push('file_complexity_distribution');
+ }
+
+ const metric = metrics.find(m => m.key === metricKey);
+ dispatch(requestMeasure(metric));
+
+ return getMeasuresAndMeta(component.key, metricsToRequest, {
+ additionalFields: 'periods'
+ }).then(r => {
+ const measures = enhanceWithLeak(r.component.measures, periodIndex);
+ const measure = measures.find(m => m.metric === metricKey);
+ const secondaryMeasure = measures.find(m => m.metric !== metricKey);
+ dispatch(receiveMeasure(measure, secondaryMeasure, r.periods));
+ });
+ };
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 QualifierIcon from '../../../../components/shared/QualifierIcon';
+import { formatLeak } from '../../utils';
+import { formatMeasure, isDiffMetric } from '../../../../helpers/measures';
+
+const Breadcrumb = ({ component, metric, onBrowse }) => {
+ const handleClick = e => {
+ e.preventDefault();
+ e.target.blur();
+ onBrowse(component);
+ };
+
+ let inner;
+ if (onBrowse) {
+ inner = (
+ <a id={'component-measures-breadcrumb-' + component.key} href="#" onClick={handleClick}>
+ {component.name}
+ </a>
+ );
+ } else {
+ inner = (
+ <span>
+ {component.name}
+ </span>
+ );
+ }
+
+ const value = isDiffMetric(metric.key)
+ ? formatLeak(component.leak, metric)
+ : formatMeasure(component.value, metric.type);
+
+ return (
+ <span>
+ <QualifierIcon qualifier={component.qualifier} />
+
+ {inner}
+ {value != null &&
+ <span>
+ {' (' + value + ')'}
+ </span>}
+ </span>
+ );
+};
+
+export default Breadcrumb;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 Breadcrumb from './Breadcrumb';
+
+const Breadcrumbs = ({ breadcrumbs, metric, onBrowse }) =>
+ <ul className="component-measures-breadcrumbs">
+ {breadcrumbs.map((component, index) =>
+ <li key={component.key}>
+ <Breadcrumb
+ component={component}
+ metric={metric}
+ onBrowse={index + 1 < breadcrumbs.length ? onBrowse : null}
+ />
+ </li>
+ )}
+ </ul>;
+
+export default Breadcrumbs;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 classNames from 'classnames';
+import QualifierIcon from '../../../../components/shared/QualifierIcon';
+import { splitPath } from '../../../../helpers/path';
+import { getComponentUrl } from '../../../../helpers/urls';
+
+const ComponentCell = ({ component, isSelected, onClick }) => {
+ const linkClassName = classNames('link-no-underline', {
+ selected: isSelected
+ });
+
+ const handleClick = e => {
+ const isLeftClickEvent = e.button === 0;
+ const isModifiedEvent = !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey);
+
+ if (isLeftClickEvent && !isModifiedEvent) {
+ e.preventDefault();
+ onClick();
+ }
+ };
+
+ let head = '';
+ let tail = component.name;
+
+ if (['DIR', 'FIL', 'UTS'].includes(component.qualifier)) {
+ const parts = splitPath(component.path);
+ head = parts.head;
+ tail = parts.tail;
+ }
+
+ const inner = (
+ <span title={component.refKey || component.key}>
+ <QualifierIcon qualifier={component.qualifier} />
+
+ {head.length > 0 &&
+ <span className="note">
+ {head}/
+ </span>}
+ <span>{tail}</span>
+ </span>
+ );
+
+ return (
+ <td style={{ maxWidth: 0 }}>
+ <div
+ style={{
+ maxWidth: '100%',
+ whiteSpace: 'nowrap',
+ overflow: 'hidden',
+ textOverflow: 'ellipsis'
+ }}>
+ {component.refId == null || component.qualifier === 'DEV_PRJ'
+ ? <a
+ id={'component-measures-component-link-' + component.key}
+ className={linkClassName}
+ href={getComponentUrl(component.key)}
+ onClick={handleClick}>
+ {inner}
+ </a>
+ : <a
+ id={'component-measures-component-link-' + component.key}
+ className={linkClassName}
+ href={getComponentUrl(component.refKey || component.key)}>
+ <span className="big-spacer-right">
+ <i className="icon-detach" />
+ </span>
+
+ {inner}
+ </a>}
+ </div>
+ </td>
+ );
+};
+
+export default ComponentCell;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 ComponentsListRow from './ComponentsListRow';
+import EmptyComponentsList from './EmptyComponentsList';
+import complementary from '../../config/complementary';
+import { getLocalizedMetricName } from '../../../../helpers/l10n';
+
+const ComponentsList = ({ components, metrics, selected, metric, onClick }) => {
+ if (!components.length) {
+ return <EmptyComponentsList />;
+ }
+
+ const otherMetrics = (complementary[metric.key] || []).map(metric => {
+ return metrics.find(m => m.key === metric);
+ });
+
+ return (
+ <table className="data zebra zebra-hover">
+ {otherMetrics.length > 0 &&
+ <thead>
+ <tr>
+ <th> </th>
+ <th className="text-right">
+ <span className="small">
+ {getLocalizedMetricName(metric)}
+ </span>
+ </th>
+ {otherMetrics.map(metric =>
+ <th key={metric.key} className="text-right">
+ <span className="small">
+ {getLocalizedMetricName(metric)}
+ </span>
+ </th>
+ )}
+ </tr>
+ </thead>}
+
+ <tbody>
+ {components.map(component =>
+ <ComponentsListRow
+ key={component.id}
+ component={component}
+ otherMetrics={otherMetrics}
+ isSelected={component === selected}
+ metric={metric}
+ onClick={onClick}
+ />
+ )}
+ </tbody>
+ </table>
+ );
+};
+
+export default ComponentsList;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 ComponentCell from './ComponentCell';
+import MeasureCell from './MeasureCell';
+
+const replaceMeasure = (component, measure) => {
+ return {
+ ...component,
+ value: measure.value,
+ leak: measure.leak
+ };
+};
+
+const ComponentsListRow = ({ component, otherMetrics, isSelected, metric, onClick }) => {
+ const handleClick = () => {
+ onClick(component);
+ };
+
+ const otherMeasures = otherMetrics.map(metric => {
+ const measure = component.measures.find(measure => measure.metric === metric.key);
+ return { ...measure, metric };
+ });
+
+ return (
+ <tr>
+ <ComponentCell
+ component={component}
+ isSelected={isSelected}
+ onClick={handleClick.bind(this, component)}
+ />
+
+ <MeasureCell component={component} metric={metric} />
+
+ {otherMeasures.map(measure =>
+ <MeasureCell
+ key={measure.metric.key}
+ component={replaceMeasure(component, measure)}
+ metric={measure.metric}
+ />
+ )}
+ </tr>
+ );
+};
+
+export default ComponentsListRow;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 { translate } from '../../../../helpers/l10n';
+
+const EmptyComponentsList = () => {
+ return (
+ <div className="note">
+ {translate('no_results')}
+ </div>
+ );
+};
+
+export default EmptyComponentsList;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 Breadcrumbs from './Breadcrumbs';
+import { translateWithParameters } from '../../../../helpers/l10n';
+
+const ListHeader = props => {
+ const { metric, breadcrumbs, onBrowse } = props;
+ const { selectedIndex, componentsCount, onSelectPrevious, onSelectNext } = props;
+ const hasPrevious = selectedIndex > 0;
+ const hasNext = selectedIndex < componentsCount - 1;
+ const blur = fn => {
+ return e => {
+ e.target.blur();
+ fn();
+ };
+ };
+
+ return (
+ <header className="measure-details-viewer-header">
+ {breadcrumbs != null &&
+ breadcrumbs.length > 1 &&
+ <div className="measure-details-header-container">
+ <Breadcrumbs breadcrumbs={breadcrumbs} metric={metric} onBrowse={onBrowse} />
+ </div>}
+
+ {selectedIndex != null &&
+ selectedIndex !== -1 &&
+ <div className="pull-right">
+ <span className="note spacer-right">
+ {translateWithParameters(
+ 'component_measures.x_of_y',
+ selectedIndex + 1,
+ componentsCount
+ )}
+ </span>
+
+ <div className="button-group">
+ {hasPrevious && <button onClick={blur(onSelectPrevious)}><</button>}
+ {hasNext && <button onClick={blur(onSelectNext)}>></button>}
+ </div>
+ </div>}
+ </header>
+ );
+};
+
+export default ListHeader;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 PropTypes from 'prop-types';
+import classNames from 'classnames';
+import moment from 'moment';
+import ComponentsList from './ComponentsList';
+import ListHeader from './ListHeader';
+import Spinner from '../../components/Spinner';
+import SourceViewer from '../../../../components/SourceViewer/SourceViewer';
+import ListFooter from '../../../../components/controls/ListFooter';
+
+export default class ListView extends React.PureComponent {
+ static contextTypes = {
+ router: PropTypes.object.isRequired
+ };
+
+ componentDidMount() {
+ const { component, metric } = this.props;
+ if (component.qualifier === 'DEV') {
+ const { router } = this.context;
+ router.replace({ pathname: `metric/${metric.key}/tree`, query: { id: component.key } });
+ }
+ this.handleChangeBaseComponent(component);
+ }
+
+ componentDidUpdate(nextProps) {
+ if (nextProps.metric !== this.props.metric) {
+ this.handleChangeBaseComponent(this.props.component);
+ }
+
+ if (this.props.selected) {
+ this.scrollToViewer();
+ } else if (this.scrollTop) {
+ this.scrollToStoredPosition();
+ }
+ }
+
+ scrollToViewer() {
+ const { container } = this.refs;
+ const top = container.getBoundingClientRect().top + window.scrollY - 95 - 10;
+
+ // scroll only to top
+ if (window.scrollY > top) {
+ window.scrollTo(0, top);
+ }
+ }
+
+ scrollToStoredPosition() {
+ window.scrollTo(0, this.scrollTop);
+ this.scrollTop = null;
+ }
+
+ storeScrollPosition() {
+ this.scrollTop = window.scrollY;
+ }
+
+ handleChangeBaseComponent(baseComponent) {
+ const { metric, onFetchList } = this.props;
+ const periodIndex = this.props.location.query.period || 1;
+ onFetchList(baseComponent, metric, Number(periodIndex));
+ }
+
+ handleFetchMore() {
+ const periodIndex = this.props.location.query.period || 1;
+ this.props.onFetchMore(Number(periodIndex));
+ }
+
+ changeSelected(selected) {
+ this.props.onSelect(selected);
+ }
+
+ handleClick(selected) {
+ this.storeScrollPosition();
+ this.props.onSelect(selected);
+ }
+
+ handleBreadcrumbClick() {
+ this.props.onSelect(undefined);
+ }
+
+ render() {
+ const {
+ component,
+ components,
+ metrics,
+ metric,
+ leakPeriod,
+ selected,
+ total,
+ fetching
+ } = this.props;
+ const { onSelectNext, onSelectPrevious } = this.props;
+
+ const breadcrumbs = [component];
+ if (selected) {
+ breadcrumbs.push(selected);
+ }
+ const selectedIndex = components.indexOf(selected);
+ const sourceViewerPeriod = metric.key.indexOf('new_') === 0 && !!leakPeriod ? leakPeriod : null;
+ const sourceViewerPeriodDate =
+ sourceViewerPeriod != null ? moment(sourceViewerPeriod.date).toDate() : null;
+
+ const filterLine =
+ sourceViewerPeriodDate != null
+ ? line => {
+ if (line.scmDate) {
+ const scmDate = moment(line.scmDate).toDate();
+ return scmDate >= sourceViewerPeriodDate;
+ } else {
+ return false;
+ }
+ }
+ : undefined;
+
+ return (
+ <div ref="container" className="measure-details-plain-list">
+ <ListHeader
+ metric={metric}
+ breadcrumbs={breadcrumbs}
+ componentsCount={components.length}
+ selectedIndex={selectedIndex}
+ onSelectPrevious={onSelectPrevious}
+ onSelectNext={onSelectNext}
+ onBrowse={this.handleBreadcrumbClick.bind(this)}
+ />
+
+ {!selected &&
+ <div className={classNames({ 'new-loading': fetching })}>
+ {!fetching || components.length !== 0
+ ? <div>
+ <ComponentsList
+ components={components}
+ metrics={metrics}
+ selected={selected}
+ metric={metric}
+ onClick={this.handleClick.bind(this)}
+ />
+ <ListFooter
+ count={components.length}
+ total={total}
+ loadMore={this.handleFetchMore.bind(this)}
+ />
+ </div>
+ : <Spinner />}
+ </div>}
+
+ {!!selected &&
+ <div className="measure-details-viewer">
+ <SourceViewer component={selected.key} filterLine={filterLine} />
+ </div>}
+ </div>
+ );
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 { connect } from 'react-redux';
+import ListView from './ListView';
+import {
+ fetchList,
+ fetchMore,
+ selectComponent,
+ selectNext,
+ selectPrevious
+} from '../../store/listViewActions';
+import {
+ getMeasuresAppListComponents,
+ getMeasuresAppListSelected,
+ getMeasuresAppListTotal,
+ getMeasuresAppListPageIndex,
+ getMeasuresAppAllMetrics,
+ getMeasuresAppDetailsMetric,
+ isMeasuresAppFetching,
+ getMeasuresAppComponent
+} from '../../../../store/rootReducer';
+
+const mapStateToProps = state => {
+ return {
+ components: getMeasuresAppListComponents(state),
+ selected: getMeasuresAppListSelected(state),
+ total: getMeasuresAppListTotal(state),
+ pageIndex: getMeasuresAppListPageIndex(state),
+ component: getMeasuresAppComponent(state),
+ metrics: getMeasuresAppAllMetrics(state),
+ metric: getMeasuresAppDetailsMetric(state),
+ fetching: isMeasuresAppFetching(state)
+ };
+};
+
+const mapDispatchToProps = dispatch => {
+ return {
+ onFetchList: (baseComponent, metric, periodIndex) =>
+ dispatch(fetchList(baseComponent, metric, periodIndex)),
+ onFetchMore: periodIndex => dispatch(fetchMore(periodIndex)),
+ onSelect: component => dispatch(selectComponent(component)),
+ onSelectNext: component => dispatch(selectNext(component)),
+ onSelectPrevious: component => dispatch(selectPrevious(component))
+ };
+};
+
+export default connect(mapStateToProps, mapDispatchToProps)(ListView);
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 Measure from '../../components/Measure';
+
+const MeasureCell = ({ component, metric }) => {
+ return (
+ <td className="thin nowrap text-right">
+ <span id={'component-measures-component-measure-' + component.key + '-' + metric.key}>
+ <Measure measure={{ value: component.value, leak: component.leak }} metric={metric} />
+ </span>
+ </td>
+ );
+};
+
+export default MeasureCell;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 IconList from './../../components/IconList';
+import IconTree from './../../components/IconTree';
+import IconBubbles from './../../components/IconBubbles';
+import IconTreemap from './../../components/IconTreemap';
+import { hasBubbleChart, hasTreemap } from '../../utils';
+import { translate } from '../../../../helpers/l10n';
+
+export default function MeasureDrilldown(props) {
+ const { children, component, metric, ...other } = props;
+
+ const child = React.cloneElement(children, { ...other });
+
+ return (
+ <div className="measure-details-drilldown">
+ <ul className="measure-details-drilldown-mode">
+ {component.qualifier !== 'DEV' &&
+ <li>
+ <Link
+ activeClassName="active"
+ to={{
+ pathname: `/component_measures_old/metric/${metric.key}/list`,
+ query: { id: component.key }
+ }}>
+ <IconList />
+ {translate('component_measures.tab.list')}
+ </Link>
+ </li>}
+
+ <li>
+ <Link
+ activeClassName="active"
+ to={{
+ pathname: `/component_measures_old/metric/${metric.key}/tree`,
+ query: { id: component.key }
+ }}>
+ <IconTree />
+ {translate('component_measures.tab.tree')}
+ </Link>
+ </li>
+
+ {hasBubbleChart(metric.key) &&
+ <li>
+ <Link
+ activeClassName="active"
+ to={{
+ pathname: `/component_measures_old/metric/${metric.key}/bubbles`,
+ query: { id: component.key }
+ }}>
+ <IconBubbles />
+ {translate('component_measures.tab.bubbles')}
+ </Link>
+ </li>}
+
+ {hasTreemap(metric) &&
+ <li>
+ <Link
+ activeClassName="active"
+ to={{
+ pathname: `/component_measures_old/metric/${metric.key}/treemap`,
+ query: { id: component.key }
+ }}>
+ <IconTreemap />
+ {translate('component_measures.tab.treemap')}
+ </Link>
+ </li>}
+ </ul>
+
+ {child}
+ </div>
+ );
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 moment from 'moment';
+import ComponentsList from './ComponentsList';
+import ListHeader from './ListHeader';
+import Spinner from '../../components/Spinner';
+import SourceViewer from '../../../../components/SourceViewer/SourceViewer';
+import ListFooter from '../../../../components/controls/ListFooter';
+
+export default class TreeView extends React.PureComponent {
+ componentDidMount() {
+ this.handleChangeBaseComponent(this.props.component);
+ }
+
+ componentDidUpdate(nextProps) {
+ if (nextProps.metric !== this.props.metric) {
+ this.handleChangeBaseComponent(this.props.component);
+ }
+
+ if (this.props.selected) {
+ this.scrollToViewer();
+ } else if (this.scrollTop) {
+ this.scrollToStoredPosition();
+ }
+ }
+
+ scrollToViewer() {
+ const { container } = this.refs;
+ const top = container.getBoundingClientRect().top + window.scrollY - 95 - 10;
+
+ // scroll only to top
+ if (window.scrollY > top) {
+ window.scrollTo(0, top);
+ }
+ }
+
+ scrollToStoredPosition() {
+ window.scrollTo(0, this.scrollTop);
+ this.scrollTop = null;
+ }
+
+ storeScrollPosition() {
+ this.scrollTop = window.scrollY;
+ }
+
+ handleChangeBaseComponent(baseComponent) {
+ const { metric, onStart } = this.props;
+ const periodIndex = this.props.location.query.period || 1;
+ onStart(baseComponent, metric, Number(periodIndex));
+ }
+
+ handleFetchMore() {
+ this.props.onFetchMore();
+ }
+
+ changeSelected(selected) {
+ this.props.onSelect(selected);
+ }
+
+ canDrilldown(component) {
+ return !['FIL', 'UTS'].includes(component.qualifier);
+ }
+
+ handleClick(selected) {
+ if (this.canDrilldown(selected)) {
+ this.props.onDrilldown(selected);
+ } else {
+ this.storeScrollPosition();
+ this.props.onSelect(selected);
+ }
+ }
+
+ handleBreadcrumbClick(component) {
+ this.props.onUseBreadcrumbs(component, this.props.metric);
+ }
+
+ render() {
+ const {
+ components,
+ metrics,
+ breadcrumbs,
+ metric,
+ leakPeriod,
+ selected,
+ total,
+ fetching
+ } = this.props;
+ const { onSelectNext, onSelectPrevious } = this.props;
+
+ const selectedIndex = components.indexOf(selected);
+ const sourceViewerPeriod = metric.key.indexOf('new_') === 0 && !!leakPeriod ? leakPeriod : null;
+ const sourceViewerPeriodDate =
+ sourceViewerPeriod != null ? moment(sourceViewerPeriod.date).toDate() : null;
+
+ const filterLine =
+ sourceViewerPeriodDate != null
+ ? line => {
+ if (line.scmDate) {
+ const scmDate = moment(line.scmDate).toDate();
+ return scmDate >= sourceViewerPeriodDate;
+ } else {
+ return false;
+ }
+ }
+ : undefined;
+
+ return (
+ <div ref="container" className="measure-details-plain-list">
+ <ListHeader
+ metric={metric}
+ breadcrumbs={breadcrumbs}
+ componentsCount={components.length}
+ selectedIndex={selectedIndex}
+ onSelectPrevious={onSelectPrevious}
+ onSelectNext={onSelectNext}
+ onBrowse={this.handleBreadcrumbClick.bind(this)}
+ />
+
+ {!selected &&
+ <div>
+ {!fetching || components.length !== 0
+ ? <div>
+ <ComponentsList
+ components={components}
+ metrics={metrics}
+ selected={selected}
+ metric={metric}
+ onClick={this.handleClick.bind(this)}
+ />
+ <ListFooter
+ count={components.length}
+ total={total}
+ loadMore={this.handleFetchMore.bind(this)}
+ />
+ </div>
+ : <Spinner />}
+ </div>}
+
+ {!!selected &&
+ <div className="measure-details-viewer">
+ <SourceViewer component={selected.key} filterLine={filterLine} />
+ </div>}
+ </div>
+ );
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 { connect } from 'react-redux';
+import TreeView from './TreeView';
+import {
+ start,
+ drilldown,
+ useBreadcrumbs,
+ fetchMore,
+ selectComponent,
+ selectNext,
+ selectPrevious
+} from '../../store/treeViewActions';
+import {
+ getMeasuresAppTreeComponents,
+ getMeasuresAppTreeBreadcrumbs,
+ getMeasuresAppTreeSelected,
+ getMeasuresAppTreeTotal,
+ getMeasuresAppTreePageIndex,
+ getMeasuresAppAllMetrics,
+ getMeasuresAppDetailsMetric,
+ isMeasuresAppFetching,
+ getMeasuresAppComponent
+} from '../../../../store/rootReducer';
+
+const mapStateToProps = state => {
+ return {
+ components: getMeasuresAppTreeComponents(state),
+ breadcrumbs: getMeasuresAppTreeBreadcrumbs(state),
+ selected: getMeasuresAppTreeSelected(state),
+ total: getMeasuresAppTreeTotal(state),
+ pageIndex: getMeasuresAppTreePageIndex(state),
+ component: getMeasuresAppComponent(state),
+ metrics: getMeasuresAppAllMetrics(state),
+ metric: getMeasuresAppDetailsMetric(state),
+ fetching: isMeasuresAppFetching(state)
+ };
+};
+
+const mapDispatchToProps = dispatch => {
+ return {
+ onStart: (rootComponent, metric, periodIndex) =>
+ dispatch(start(rootComponent, metric, periodIndex)),
+ onDrilldown: component => dispatch(drilldown(component)),
+ onUseBreadcrumbs: component => dispatch(useBreadcrumbs(component)),
+ onFetchMore: () => dispatch(fetchMore()),
+ onSelect: component => dispatch(selectComponent(component)),
+ onSelectNext: component => dispatch(selectNext(component)),
+ onSelectPrevious: component => dispatch(selectPrevious(component))
+ };
+};
+
+export default connect(mapStateToProps, mapDispatchToProps)(TreeView);
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 { DISPLAY_HOME } from '../app/actions';
+import { REQUEST_MEASURE, RECEIVE_MEASURE } from './actions';
+
+const initialState = {
+ metric: undefined,
+ secondaryMeasure: undefined,
+ measure: undefined,
+ periods: undefined
+};
+
+export default function appReducer(state = initialState, action = {}) {
+ switch (action.type) {
+ case DISPLAY_HOME:
+ return initialState;
+ case REQUEST_MEASURE:
+ return { ...state, metric: action.metric };
+ case RECEIVE_MEASURE:
+ return {
+ ...state,
+ measure: action.measure,
+ secondaryMeasure: action.secondaryMeasure,
+ periods: action.periods
+ };
+ default:
+ return state;
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 { scaleLinear, scaleOrdinal } from 'd3-scale';
+import Spinner from './../../components/Spinner';
+import { getLeakValue } from '../../utils';
+import { Treemap } from '../../../../components/charts/treemap';
+import { getChildren } from '../../../../api/components';
+import { formatMeasure } from '../../../../helpers/measures';
+import {
+ translate,
+ translateWithParameters,
+ getLocalizedMetricName
+} from '../../../../helpers/l10n';
+import { getComponentUrl } from '../../../../helpers/urls';
+import Workspace from '../../../../components/workspace/main';
+
+const HEIGHT = 500;
+
+export default class MeasureTreemap extends React.PureComponent {
+ state = {
+ fetching: true,
+ components: [],
+ breadcrumbs: []
+ };
+
+ componentDidMount() {
+ const { component } = this.props;
+
+ this.mounted = true;
+ this.fetchComponents(component.key);
+ }
+
+ componentDidUpdate(nextProps) {
+ if (nextProps.metric !== this.props.metric) {
+ this.fetchComponents(this.props.component.key);
+ }
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
+ fetchComponents(componentKey) {
+ const { metric } = this.props;
+ const metrics = ['ncloc', metric.key];
+ const options = {
+ s: 'metric',
+ metricSort: 'ncloc',
+ asc: false
+ };
+
+ return getChildren(componentKey, metrics, options).then(r => {
+ const components = r.components.map(component => {
+ const measures = {};
+ const key = component.refKey || component.key;
+
+ component.measures.forEach(measure => {
+ const shouldUseLeak = measure.metric.indexOf('new_') === 0;
+ measures[measure.metric] = shouldUseLeak ? getLeakValue(measure) : measure.value;
+ });
+ return { ...component, measures, key };
+ });
+
+ this.setState({
+ components,
+ fetching: false
+ });
+ });
+ }
+
+ getTooltip(component) {
+ const { metric } = this.props;
+
+ let inner = [
+ component.name,
+ `${translate('metric.ncloc.name')}: ${formatMeasure(component.measures['ncloc'], 'INT')}`
+ ];
+
+ const colorMeasure = component.measures[metric.key];
+ const formatted = colorMeasure != null ? formatMeasure(colorMeasure, metric.type) : '—';
+ inner.push(`${getLocalizedMetricName(metric)}: ${formatted}`);
+ inner = inner.join('<br>');
+ return `<div class="text-left">${inner}</div>`;
+ }
+ getPercentColorScale(metric) {
+ const color = scaleLinear().domain([0, 25, 50, 75, 100]);
+ color.range(
+ metric.direction === 1
+ ? ['#d4333f', '#ed7d20', '#eabe06', '#b0d513', '#00aa00']
+ : ['#00aa00', '#b0d513', '#eabe06', '#ed7d20', '#d4333f']
+ );
+ return color;
+ }
+ getRatingColorScale() {
+ return scaleLinear()
+ .domain([1, 2, 3, 4, 5])
+ .range(['#00aa00', '#b0d513', '#eabe06', '#ed7d20', '#d4333f']);
+ }
+ getLevelColorScale() {
+ return scaleOrdinal()
+ .domain(['ERROR', 'WARN', 'OK', 'NONE'])
+ .range(['#d4333f', '#ed7d20', '#00aa00', '#b4b4b4']);
+ }
+ getScale() {
+ const { metric } = this.props;
+ if (metric.type === 'LEVEL') {
+ return this.getLevelColorScale();
+ }
+ if (metric.type === 'RATING') {
+ return this.getRatingColorScale();
+ }
+ return this.getPercentColorScale(metric);
+ }
+ handleRectangleClick(node) {
+ const isFile = node.qualifier === 'FIL' || node.qualifier === 'UTS';
+ if (isFile) {
+ Workspace.openComponent({ key: node.key });
+ return;
+ }
+ this.fetchComponents(node.key).then(() => {
+ let nextBreadcrumbs = [...this.state.breadcrumbs];
+ const index = this.state.breadcrumbs.findIndex(b => b.key === node.key);
+ if (index !== -1) {
+ nextBreadcrumbs = nextBreadcrumbs.slice(0, index);
+ }
+ nextBreadcrumbs = [
+ ...nextBreadcrumbs,
+ {
+ key: node.key,
+ name: node.name,
+ qualifier: node.qualifier
+ }
+ ];
+ this.setState({ breadcrumbs: nextBreadcrumbs });
+ });
+ }
+ handleReset() {
+ const { component } = this.props;
+ this.fetchComponents(component.key).then(() => {
+ this.setState({ breadcrumbs: [] });
+ });
+ }
+ renderTreemap() {
+ const { metric } = this.props;
+ const colorScale = this.getScale();
+ const items = this.state.components
+ .filter(component => component.measures['ncloc'])
+ .map(component => {
+ const colorMeasure = component.measures[metric.key];
+ return {
+ id: component.id,
+ key: component.key,
+ name: component.name,
+ qualifier: component.qualifier,
+ size: component.measures['ncloc'],
+ color: colorMeasure != null ? colorScale(colorMeasure) : '#777',
+ tooltip: this.getTooltip(component),
+ label: component.name,
+ link: getComponentUrl(component.key)
+ };
+ });
+ return (
+ <Treemap
+ items={items}
+ breadcrumbs={this.state.breadcrumbs}
+ height={HEIGHT}
+ canBeClicked={() => true}
+ onRectangleClick={this.handleRectangleClick.bind(this)}
+ onReset={this.handleReset.bind(this)}
+ />
+ );
+ }
+ render() {
+ const { metric } = this.props;
+ const { fetching } = this.state;
+ if (fetching) {
+ return (
+ <div className="measure-details-treemap">
+ <div className="note text-center" style={{ lineHeight: `${HEIGHT}px` }}>
+ <Spinner />
+ </div>
+ </div>
+ );
+ }
+ return (
+ <div className="measure-details-treemap">
+ <ul className="list-inline note measure-details-treemap-legend">
+ <li>
+ {translateWithParameters(
+ 'component_measures.legend.color_x',
+ getLocalizedMetricName(metric)
+ )}
+ </li>
+ <li>
+ {translateWithParameters(
+ 'component_measures.legend.size_x',
+ translate('metric.ncloc.name')
+ )}
+ </li>
+ </ul>
+ {this.renderTreemap()}
+ </div>
+ );
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 { connect } from 'react-redux';
+import MeasureTreemap from './MeasureTreemap';
+import {
+ getMeasuresAppDetailsMetric,
+ getMeasuresAppComponent
+} from '../../../../store/rootReducer';
+
+const mapStateToProps = state => {
+ return {
+ component: getMeasuresAppComponent(state),
+ metric: getMeasuresAppDetailsMetric(state)
+ };
+};
+
+const mapDispatchToProps = () => {
+ return {};
+};
+
+export default connect(mapStateToProps, mapDispatchToProps)(MeasureTreemap);
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 AllMeasuresDomain from './AllMeasuresDomain';
+import { getLeakPeriodLabel } from '../../../helpers/periods';
+
+export default function AllMeasures(props) {
+ const { component, domains, periods } = props;
+ const leakPeriodLabel = getLeakPeriodLabel(periods);
+
+ return (
+ <ul className="measures-domains">
+ {domains.map(domain =>
+ <AllMeasuresDomain
+ key={domain.name}
+ domain={domain}
+ component={component}
+ leakPeriodLabel={leakPeriodLabel}
+ />
+ )}
+ </ul>
+ );
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 { connect } from 'react-redux';
+import AllMeasures from './AllMeasures';
+import {
+ getMeasuresAppHomeDomains,
+ getMeasuresAppHomePeriods,
+ getMeasuresAppComponent
+} from '../../../store/rootReducer';
+
+const mapStateToProps = state => {
+ return {
+ component: getMeasuresAppComponent(state),
+ domains: getMeasuresAppHomeDomains(state),
+ periods: getMeasuresAppHomePeriods(state)
+ };
+};
+
+export default connect(mapStateToProps)(AllMeasures);
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 PropTypes from 'prop-types';
+import HomeMeasuresList from './HomeMeasuresList';
+import { getLocalizedMetricDomain } from '../../../helpers/l10n';
+
+export default function AllMeasuresDomain(props) {
+ const { domain, component, displayHeader } = props;
+
+ return (
+ <li>
+ {displayHeader &&
+ <header className="page-header">
+ <h3 className="page-title">
+ {getLocalizedMetricDomain(domain.name)}
+ </h3>
+ </header>}
+
+ <HomeMeasuresList domain={domain} component={component} />
+ </li>
+ );
+}
+
+AllMeasuresDomain.defaultProps = {
+ displayHeader: true
+};
+
+AllMeasuresDomain.propTypes = {
+ displayHeader: PropTypes.bool
+};
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 HomeMeasuresList from './HomeMeasuresList';
+import MeasureBubbleChartContainer from '../components/bubbleChart/MeasureBubbleChartContainer';
+import { hasBubbleChart } from '../utils';
+
+export default function DomainMeasures(props) {
+ const { component, domains } = props;
+ const { domainName } = props.params;
+ const domain = domains.find(d => d.name === domainName);
+
+ return (
+ <section id="component-measures-domain">
+ <HomeMeasuresList domain={domain} component={component} />
+
+ {hasBubbleChart(domainName) && <MeasureBubbleChartContainer domainName={domainName} />}
+ </section>
+ );
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 { connect } from 'react-redux';
+import DomainMeasures from './DomainMeasures';
+import {
+ getMeasuresAppHomeDomains,
+ getMeasuresAppHomePeriods,
+ getMeasuresAppComponent
+} from '../../../store/rootReducer';
+
+const mapStateToProps = state => {
+ return {
+ component: getMeasuresAppComponent(state),
+ domains: getMeasuresAppHomeDomains(state),
+ periods: getMeasuresAppHomePeriods(state)
+ };
+};
+
+export default connect(mapStateToProps)(DomainMeasures);
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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, IndexLink } from 'react-router';
+import LeakPeriodLegend from '../components/LeakPeriodLegend';
+import { getLeakPeriod } from '../../../helpers/periods';
+import { translate, getLocalizedMetricDomain } from '../../../helpers/l10n';
+
+export default class Home extends React.PureComponent {
+ componentDidMount() {
+ document.querySelector('html').classList.add('dashboard-page');
+ this.props.onDisplay();
+ this.props.fetchMeasures();
+ }
+
+ componentWillUnmount() {
+ document.querySelector('html').classList.remove('dashboard-page');
+ }
+
+ render() {
+ const { component, domains, periods } = this.props;
+
+ if (domains == null) {
+ return null;
+ }
+
+ const leakPeriod = getLeakPeriod(periods);
+
+ return (
+ <section id="component-measures-home" className="page page-container page-limited">
+ <header id="component-measures-home-header" className="home-header">
+ <nav className="nav-pills pull-left">
+ <ul>
+ <li>
+ <IndexLink
+ to={{ pathname: '/component_measures_old', query: { id: component.key } }}
+ activeClassName="active">
+ {translate('all')}
+ </IndexLink>
+ </li>
+ {domains.map(domain =>
+ <li key={domain.name}>
+ <Link
+ to={{
+ pathname: `/component_measures_old/domain/${domain.name}`,
+ query: { id: component.key }
+ }}
+ activeClassName="active">
+ {getLocalizedMetricDomain(domain.name)}
+ </Link>
+ </li>
+ )}
+ </ul>
+ </nav>
+
+ {leakPeriod != null && <LeakPeriodLegend component={component} period={leakPeriod} />}
+ </header>
+
+ <main id="component-measures-home-main">
+ {this.props.children}
+ </main>
+ </section>
+ );
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 { connect } from 'react-redux';
+import Home from './Home';
+import { fetchMeasures } from './actions';
+import { displayHome } from '../app/actions';
+import {
+ getMeasuresAppHomeDomains,
+ getMeasuresAppHomePeriods,
+ getMeasuresAppComponent
+} from '../../../store/rootReducer';
+
+const mapStateToProps = state => {
+ return {
+ component: getMeasuresAppComponent(state),
+ domains: getMeasuresAppHomeDomains(state),
+ periods: getMeasuresAppHomePeriods(state)
+ };
+};
+
+const mapDispatchToProps = dispatch => {
+ return {
+ onDisplay: () => dispatch(displayHome()),
+ fetchMeasures: () => dispatch(fetchMeasures())
+ };
+};
+
+export default connect(mapStateToProps, mapDispatchToProps)(Home);
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 { partition, sortBy } from 'lodash';
+import MeasuresList from './MeasuresList';
+import { domains } from '../config/domains';
+import { getLocalizedMetricName } from '../../../helpers/l10n';
+
+function sortMeasures(measures, order) {
+ const [known, unknown] = partition(measures, measure => order.includes(measure.metric.key));
+ return [
+ ...sortBy(known, measure => order.indexOf(measure.metric.key)),
+ ...sortBy(unknown, measure => getLocalizedMetricName(measure.metric))
+ ];
+}
+
+function filterIssuesMeasures(measures) {
+ const BANNED_MEASURES = [
+ 'blocker_violations',
+ 'new_blocker_violations',
+ 'critical_violations',
+ 'new_critical_violations',
+ 'major_violations',
+ 'new_major_violations',
+ 'minor_violations',
+ 'new_minor_violations',
+ 'info_violations',
+ 'new_info_violations'
+ ];
+ return measures.filter(measure => !BANNED_MEASURES.includes(measure.metric.key));
+}
+
+const HomeMeasuresList = ({ domain, component }) => {
+ const { measures, name } = domain;
+ const config = domains[name] || {};
+
+ const filteredMeasures = filterIssuesMeasures(measures);
+
+ const configMain = config.main || [];
+ const [mainMeasures, otherMeasures] = partition(filteredMeasures, measure =>
+ configMain.includes(measure.metric.key)
+ );
+
+ const configOrder = config.order || [];
+ const sortedMainMeasures = sortMeasures(mainMeasures, configOrder);
+ const sortedOtherMeasures = sortMeasures(otherMeasures, configOrder);
+
+ return (
+ <div className="home-measures-list clearfix">
+ {sortedMainMeasures.length > 0 &&
+ <MeasuresList
+ className="main-domain-measures"
+ measures={sortedMainMeasures}
+ component={component}
+ spaces={[]}
+ />}
+
+ {sortedOtherMeasures.length > 0 &&
+ <MeasuresList measures={sortedOtherMeasures} component={component} spaces={[]} />}
+ </div>
+ );
+};
+
+export default HomeMeasuresList;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 Measure from '../components/Measure';
+import { isDiffMetric } from '../../../helpers/measures';
+
+const MeasureListValue = ({ measure }) => {
+ const { metric } = measure;
+
+ if (isDiffMetric(metric.key)) {
+ return (
+ <div
+ id={`measure-${measure.metric.key}-leak`}
+ className="domain-measures-value domain-measures-leak">
+ <Measure measure={measure} />
+ </div>
+ );
+ }
+
+ return (
+ <div id={`measure-${measure.metric.key}-value`} className="domain-measures-value">
+ <Measure measure={measure} />
+ </div>
+ );
+};
+
+export default MeasureListValue;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 MeasureListValue from './MeasureListValue';
+import { getLocalizedMetricName } from '../../../helpers/l10n';
+import IssueTypeIcon from '../../../components/ui/IssueTypeIcon';
+
+const MeasuresList = ({ measures, component, className = 'domain-measures' }) => {
+ return (
+ <ul className={className}>
+ {measures.map(measure =>
+ <li key={measure.metric.key} id={`measure-${measure.metric.key}`}>
+ <Link
+ to={{
+ pathname: `/component_measures_old/metric/${measure.metric.key}`,
+ query: { id: component.key }
+ }}>
+ <div className="domain-measures-name">
+ <IssueTypeIcon query={measure.metric.key} className="little-spacer-right" />
+ <span id={`measure-${measure.metric.key}-name`}>
+ {getLocalizedMetricName(measure.metric)}
+ </span>
+ </div>
+
+ <MeasureListValue measure={measure} />
+ </Link>
+ </li>
+ )}
+ </ul>
+ );
+};
+
+export default MeasuresList;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 { startFetching, stopFetching } from '../store/statusActions';
+import { getMeasuresAndMeta } from '../../../api/measures';
+import { getLeakValue } from '../utils';
+import { getMeasuresAppComponent, getMeasuresAppAllMetrics } from '../../../store/rootReducer';
+
+export const RECEIVE_MEASURES = 'measuresApp/home/RECEIVE_MEASURES';
+
+export function receiveMeasures(measures, periods) {
+ return { type: RECEIVE_MEASURES, measures, periods };
+}
+
+function banQualityGate(component, measures) {
+ let newMeasures = [...measures];
+
+ if (!['VW', 'SVW', 'APP'].includes(component.qualifier)) {
+ newMeasures = newMeasures.filter(measure => measure.metric !== 'alert_status');
+ }
+
+ if (component.qualifier === 'APP') {
+ newMeasures = newMeasures.filter(
+ measure =>
+ measure.metric !== 'releasability_rating' && measure.metric !== 'releasability_effort'
+ );
+ }
+
+ return newMeasures;
+}
+
+export function fetchMeasures() {
+ return (dispatch, getState) => {
+ dispatch(startFetching());
+
+ const state = getState();
+ const component = getMeasuresAppComponent(state);
+ const metrics = getMeasuresAppAllMetrics(state);
+
+ const metricKeys = metrics
+ .filter(metric => !metric.hidden)
+ .filter(metric => metric.type !== 'DATA' && metric.type !== 'DISTRIB')
+ .map(metric => metric.key);
+
+ getMeasuresAndMeta(component.key, metricKeys, { additionalFields: 'periods' }).then(r => {
+ const measures = banQualityGate(component, r.component.measures)
+ .map(measure => {
+ const metric = metrics.find(metric => metric.key === measure.metric);
+ const leak = getLeakValue(measure);
+ return { ...measure, metric, leak };
+ })
+ .filter(measure => {
+ const hasValue = measure.value != null;
+ const hasLeakValue = measure.leak != null;
+ return hasValue || hasLeakValue;
+ });
+
+ const newBugs = measures.find(measure => measure.metric.key === 'new_bugs');
+
+ const applicationPeriods = newBugs ? [{ index: 1 }] : [];
+ const periods = component.qualifier === 'APP' ? applicationPeriods : r.periods;
+
+ dispatch(receiveMeasures(measures, periods));
+ dispatch(stopFetching());
+ });
+ };
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 { groupBy, partition, sortBy, toPairs } from 'lodash';
+import { RECEIVE_MEASURES } from './actions';
+import { getLocalizedMetricName } from '../../../helpers/l10n';
+
+const initialState = {
+ measures: undefined,
+ domains: undefined,
+ periods: undefined
+};
+
+function groupByDomains(measures) {
+ const KNOWN_DOMAINS = [
+ 'Releasability',
+ 'Reliability',
+ 'Security',
+ 'Maintainability',
+ 'Coverage',
+ 'Duplications',
+ 'Size',
+ 'Complexity'
+ ];
+
+ const domains = sortBy(
+ toPairs(groupBy(measures, measure => measure.metric.domain)).map(r => {
+ const [name, measures] = r;
+ const sortedMeasures = sortBy(measures, measure => getLocalizedMetricName(measure.metric));
+
+ return { name, measures: sortedMeasures };
+ }),
+ 'name'
+ );
+ const [knownDomains, unknownDomains] = partition(domains, domain =>
+ KNOWN_DOMAINS.includes(domain.name)
+ );
+ return [
+ ...sortBy(knownDomains, domain => KNOWN_DOMAINS.indexOf(domain.name)),
+ ...sortBy(unknownDomains, domain => domain.name)
+ ];
+}
+
+export default function(state = initialState, action = {}) {
+ switch (action.type) {
+ case RECEIVE_MEASURES:
+ return {
+ ...state,
+ measures: action.measures,
+ domains: groupByDomains(action.measures),
+ periods: action.periods
+ };
+ default:
+ return state;
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 { isDiffMetric } from '../../helpers/measures';
+
+export function checkHistoryExistence(nextState, replace) {
+ const { metricKey } = nextState.params;
+
+ if (isDiffMetric(metricKey)) {
+ replace({
+ pathname: metricKey,
+ query: nextState.location.query
+ });
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.
+ */
+const routes = [
+ {
+ getComponent(_, callback) {
+ import('./app/AppContainer').then(i => callback(null, i.default));
+ },
+ childRoutes: [
+ {
+ getComponent(_, callback) {
+ import('./home/HomeContainer').then(i => callback(null, i.default));
+ },
+ childRoutes: [
+ {
+ getIndexRoute(_, callback) {
+ import('./home/AllMeasuresContainer').then(i =>
+ callback(null, { component: i.default })
+ );
+ }
+ },
+ {
+ path: 'domain/:domainName',
+ getComponent(_, callback) {
+ import('./home/DomainMeasuresContainer').then(i => callback(null, i.default));
+ }
+ }
+ ]
+ },
+ {
+ path: 'metric/:metricKey',
+ getComponent(_, callback) {
+ import('./details/MeasureDetailsContainer').then(i => callback(null, i.default));
+ },
+ childRoutes: [
+ {
+ indexRoute: {
+ onEnter(nextState, replace) {
+ const { params, location } = nextState;
+ replace({
+ pathname: `/component_measures_old/metric/${params.metricKey}/list`,
+ query: location.query
+ });
+ }
+ }
+ },
+ {
+ path: 'list',
+ getComponent(_, callback) {
+ import('./details/drilldown/ListViewContainer').then(i => callback(null, i.default));
+ }
+ },
+ {
+ path: 'tree',
+ getComponent(_, callback) {
+ import('./details/drilldown/TreeViewContainer').then(i => callback(null, i.default));
+ }
+ },
+ {
+ path: 'history',
+ onEnter(nextState, replace) {
+ replace({
+ pathname: '/project/activity',
+ query: {
+ id: nextState.location.query.id,
+ graph: 'custom',
+ custom_metrics: nextState.params.metricKey
+ }
+ });
+ }
+ },
+ {
+ path: 'treemap',
+ getComponent(_, callback) {
+ import('./details/treemap/MeasureTreemapContainer').then(i =>
+ callback(null, i.default)
+ );
+ }
+ }
+ ]
+ }
+ ]
+ }
+];
+
+export default routes;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 { getComponentTree } from '../../../api/components';
+import { enhanceWithMeasure } from '../utils';
+import { startFetching, stopFetching } from './statusActions';
+import complementary from '../config/complementary';
+import { getMeasuresAppList } from '../../../store/rootReducer';
+
+export const UPDATE_STORE = 'measuresApp/drilldown/list/UPDATE_STORE';
+
+function updateStore(state) {
+ return { type: UPDATE_STORE, state };
+}
+
+function getComplementary(metric) {
+ const comp = complementary[metric] || [];
+ return [metric, ...comp];
+}
+
+function makeRequest(baseComponent, metric, options, periodIndex = 1) {
+ const asc = metric.direction === 1;
+ const ps = 100;
+ const finalOptions = { asc, ps, metricSortFilter: 'withMeasuresOnly' };
+
+ if (metric.key.indexOf('new_') === 0) {
+ Object.assign(options, {
+ s: 'metricPeriod,name',
+ metricSort: metric.key,
+ metricPeriodSort: periodIndex
+ });
+ } else {
+ Object.assign(options, {
+ s: 'metric,name',
+ metricSort: metric.key
+ });
+ }
+
+ Object.assign(finalOptions, options);
+ return getComponentTree('leaves', baseComponent.key, getComplementary(metric.key), finalOptions);
+}
+
+function fetchLeaves(baseComponent, metric, pageIndex = 1, periodIndex = 1) {
+ const options = { p: pageIndex };
+
+ return makeRequest(baseComponent, metric, options, periodIndex).then(r => {
+ const nextComponents = enhanceWithMeasure(r.components, metric.key, periodIndex);
+
+ return {
+ components: nextComponents,
+ pageIndex: r.paging.pageIndex,
+ total: r.paging.total
+ };
+ });
+}
+
+/**
+ * Fetch the first page of components for a given base component
+ * @param baseComponent
+ * @param metric
+ * @param periodIndex
+ */
+export function fetchList(baseComponent, metric, periodIndex = 1) {
+ return (dispatch, getState) => {
+ const list = getMeasuresAppList(getState());
+ if (list.baseComponent === baseComponent && list.metric === metric) {
+ return Promise.resolve();
+ }
+
+ dispatch(startFetching());
+ return fetchLeaves(baseComponent, metric, 1, periodIndex).then(r => {
+ dispatch(
+ updateStore({
+ ...r,
+ baseComponent,
+ metric
+ })
+ );
+ dispatch(stopFetching());
+ });
+ };
+}
+
+export function fetchMore(periodIndex) {
+ return (dispatch, getState) => {
+ const { baseComponent, metric, pageIndex, components } = getMeasuresAppList(getState());
+ dispatch(startFetching());
+ return fetchLeaves(baseComponent, metric, pageIndex + 1, periodIndex).then(r => {
+ const nextComponents = [...components, ...r.components];
+ dispatch(updateStore({ ...r, components: nextComponents }));
+ dispatch(stopFetching());
+ });
+ };
+}
+
+/**
+ * Select specified component from the list
+ * @param component A component to select
+ */
+export function selectComponent(component) {
+ return dispatch => {
+ dispatch(updateStore({ selected: component }));
+ };
+}
+
+/**
+ * Select next element from the list of components
+ */
+export function selectNext() {
+ return (dispatch, getState) => {
+ const { components, selected } = getMeasuresAppList(getState());
+ const selectedIndex = components.indexOf(selected);
+ if (selectedIndex < components.length - 1) {
+ const nextSelected = components[selectedIndex + 1];
+ dispatch(selectComponent(nextSelected));
+ }
+ };
+}
+
+/**
+ * Select previous element from the list of components
+ */
+export function selectPrevious() {
+ return (dispatch, getState) => {
+ const { components, selected } = getMeasuresAppList(getState());
+ const selectedIndex = components.indexOf(selected);
+ if (selectedIndex > 0) {
+ const nextSelected = components[selectedIndex - 1];
+ dispatch(selectComponent(nextSelected));
+ }
+ };
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 { DISPLAY_HOME } from './../app/actions';
+import { UPDATE_STORE } from './listViewActions';
+
+const initialState = {
+ components: [],
+ total: 0
+};
+
+export default function drilldownReducer(state = initialState, action = {}) {
+ switch (action.type) {
+ case DISPLAY_HOME:
+ return initialState;
+ case UPDATE_STORE:
+ return { ...state, ...action.state };
+ default:
+ return state;
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 { combineReducers } from 'redux';
+import appReducer from './../app/reducer';
+import statusReducer from './statusReducer';
+import homeReducer from '../home/reducer';
+import detailsReducer from '../details/reducer';
+import listViewReducer from './listViewReducer';
+import treeViewReducer from './treeViewReducer';
+
+export default combineReducers({
+ app: appReducer,
+ home: homeReducer,
+ details: detailsReducer,
+ list: listViewReducer,
+ tree: treeViewReducer,
+ status: statusReducer
+});
+
+export const getComponent = state => state.app.component;
+
+export const getAllMetrics = state => state.app.metrics;
+
+export const getDetailsMetric = state => state.details.metric;
+
+export const getDetailsMeasure = state => state.details.measure;
+
+export const getDetailsSecondaryMeasure = state => state.details.secondaryMeasure;
+
+export const getDetailsPeriods = state => state.details.periods;
+
+export const isFetching = state => state.status.fetching;
+
+export const getList = state => state.list;
+
+export const getListComponents = state => state.list.components;
+
+export const getListSelected = state => state.list.selected;
+
+export const getListTotal = state => state.list.total;
+
+export const getListPageIndex = state => state.list.pageIndex;
+
+export const getTree = state => state.tree;
+
+export const getTreeComponents = state => state.tree.components;
+
+export const getTreeBreadcrumbs = state => state.tree.breadcrumbs;
+
+export const getTreeSelected = state => state.tree.selected;
+
+export const getTreeTotal = state => state.tree.total;
+
+export const getTreePageIndex = state => state.tree.pageIndex;
+
+export const getHomeDomains = state => state.home.domains;
+
+export const getHomePeriods = state => state.home.periods;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.
+ */
+export const START_FETCHING = 'measuresApp/status/START_FETCHING';
+export const STOP_FETCHING = 'measuresApp/status/STOP_FETCHING';
+
+export function startFetching() {
+ return { type: START_FETCHING };
+}
+
+export function stopFetching() {
+ return { type: STOP_FETCHING };
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 { START_FETCHING, STOP_FETCHING } from './statusActions';
+
+const initialState = {
+ fetching: false
+};
+
+export default function drilldownReducer(state = initialState, action = {}) {
+ switch (action.type) {
+ case START_FETCHING:
+ return { ...state, fetching: true };
+ case STOP_FETCHING:
+ return { ...state, fetching: false };
+ default:
+ return state;
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 { initial } from 'lodash';
+import { getComponentTree } from '../../../api/components';
+import { enhanceWithMeasure } from '../utils';
+import { startFetching, stopFetching } from './statusActions';
+import complementary from '../config/complementary';
+import { getMeasuresAppTree } from '../../../store/rootReducer';
+
+/*
+ * Actions
+ */
+
+export const UPDATE_STORE = 'measuresApp/drilldown/tree/UPDATE_STORE';
+export const INIT = 'measuresApp/drilldown/tree/INIT';
+
+/*
+ * Action Creators
+ */
+
+/**
+ * Internal
+ * Update store
+ * @param state
+ * @returns {{type: string, state: *}}
+ */
+function updateStore(state) {
+ return { type: UPDATE_STORE, state };
+}
+
+/**
+ * Init tree view drilldown for the given root component and given metric
+ * @param rootComponent
+ * @param metric
+ * @param periodIndex
+ * @returns {{type: string, rootComponent: *, metric: *}}
+ */
+function init(rootComponent, metric, periodIndex = 1) {
+ return { type: INIT, rootComponent, metric, periodIndex };
+}
+
+/*
+ * Workflow
+ */
+
+function getComplementary(metric) {
+ const comp = complementary[metric] || [];
+ return [metric, ...comp];
+}
+
+function makeRequest(rootComponent, baseComponent, metric, options, periodIndex = 1) {
+ const asc = metric.direction === 1;
+ const ps = 100;
+ const finalOptions = { asc, ps, metricSortFilter: 'withMeasuresOnly' };
+
+ if (metric.key.indexOf('new_') === 0) {
+ Object.assign(options, {
+ s: 'metricPeriod,name',
+ metricSort: metric.key,
+ metricPeriodSort: periodIndex
+ });
+ } else {
+ Object.assign(options, {
+ s: 'metric,name',
+ metricSort: metric.key
+ });
+ }
+
+ if (rootComponent.qualifier === 'DEV' && baseComponent.qualifier !== 'DEV') {
+ Object.assign(options, { developerId: rootComponent.id });
+ }
+
+ Object.assign(finalOptions, options);
+
+ const finalKey = baseComponent.refKey || baseComponent.key;
+
+ return getComponentTree('children', finalKey, getComplementary(metric.key), finalOptions);
+}
+
+function fetchComponents(rootComponent, baseComponent, metric, pageIndex = 1, periodIndex = 1) {
+ const options = { p: pageIndex };
+
+ return makeRequest(rootComponent, baseComponent, metric, options, periodIndex).then(r => {
+ const nextComponents = enhanceWithMeasure(r.components, metric.key, periodIndex);
+
+ return {
+ baseComponent,
+ components: nextComponents,
+ pageIndex: r.paging.pageIndex,
+ total: r.paging.total
+ };
+ });
+}
+
+/**
+ * Fetch the first page of components for a given base component
+ * @param baseComponent
+ */
+function fetchList(baseComponent) {
+ return (dispatch, getState) => {
+ const { metric, periodIndex, rootComponent } = getMeasuresAppTree(getState());
+
+ dispatch(startFetching());
+ return fetchComponents(rootComponent, baseComponent, metric, 1, periodIndex).then(r => {
+ dispatch(
+ updateStore({
+ ...r,
+ baseComponent,
+ breadcrumbs: [baseComponent]
+ })
+ );
+ dispatch(stopFetching());
+ });
+ };
+}
+
+/**
+ * Init tree view with root component and metric.
+ * Fetch the first page of components if needed.
+ * @param rootComponent
+ * @param metric
+ * @param periodIndex
+ * @returns {function()}
+ */
+export function start(rootComponent, metric, periodIndex = 1) {
+ return (dispatch, getState) => {
+ const tree = getMeasuresAppTree(getState());
+ if (rootComponent === tree.rootComponent && metric === tree.metric) {
+ return Promise.resolve();
+ }
+
+ dispatch(init(rootComponent, metric, periodIndex));
+ dispatch(fetchList(rootComponent));
+ };
+}
+
+/**
+ * Drilldown to the component
+ * @param component
+ */
+export function drilldown(component) {
+ return (dispatch, getState) => {
+ const { metric, rootComponent, breadcrumbs, periodIndex } = getMeasuresAppTree(getState());
+ dispatch(startFetching());
+ return fetchComponents(rootComponent, component, metric, 1, periodIndex).then(r => {
+ dispatch(
+ updateStore({
+ ...r,
+ breadcrumbs: [...breadcrumbs, component],
+ selected: undefined
+ })
+ );
+ dispatch(stopFetching());
+ });
+ };
+}
+
+/**
+ * Go up using breadcrumbs
+ * @param component
+ */
+export function useBreadcrumbs(component) {
+ return (dispatch, getState) => {
+ const { metric, rootComponent, breadcrumbs, periodIndex } = getMeasuresAppTree(getState());
+ const index = breadcrumbs.indexOf(component);
+ dispatch(startFetching());
+ return fetchComponents(rootComponent, component, metric, 1, periodIndex).then(r => {
+ dispatch(
+ updateStore({
+ ...r,
+ breadcrumbs: breadcrumbs.slice(0, index + 1),
+ selected: undefined
+ })
+ );
+ dispatch(stopFetching());
+ });
+ };
+}
+
+export function fetchMore() {
+ return (dispatch, getState) => {
+ const {
+ rootComponent,
+ baseComponent,
+ metric,
+ pageIndex,
+ components,
+ periodIndex
+ } = getMeasuresAppTree(getState());
+ dispatch(startFetching());
+ return fetchComponents(
+ rootComponent,
+ baseComponent,
+ metric,
+ pageIndex + 1,
+ periodIndex
+ ).then(r => {
+ const nextComponents = [...components, ...r.components];
+ dispatch(updateStore({ ...r, components: nextComponents }));
+ dispatch(stopFetching());
+ });
+ };
+}
+
+/**
+ * Select given component from the list
+ * @param component
+ */
+export function selectComponent(component) {
+ return (dispatch, getState) => {
+ const { breadcrumbs } = getMeasuresAppTree(getState());
+ const nextBreadcrumbs = [...breadcrumbs, component];
+ dispatch(
+ updateStore({
+ selected: component,
+ breadcrumbs: nextBreadcrumbs
+ })
+ );
+ };
+}
+
+/**
+ * Select next element from the list of components
+ */
+export function selectNext() {
+ return (dispatch, getState) => {
+ const { components, selected, breadcrumbs } = getMeasuresAppTree(getState());
+ const selectedIndex = components.indexOf(selected);
+ if (selectedIndex < components.length - 1) {
+ const nextSelected = components[selectedIndex + 1];
+ const nextBreadcrumbs = [...initial(breadcrumbs), nextSelected];
+ dispatch(
+ updateStore({
+ selected: nextSelected,
+ breadcrumbs: nextBreadcrumbs
+ })
+ );
+ }
+ };
+}
+
+/**
+ * Select previous element from the list of components
+ */
+export function selectPrevious() {
+ return (dispatch, getState) => {
+ const { components, selected, breadcrumbs } = getMeasuresAppTree(getState());
+ const selectedIndex = components.indexOf(selected);
+ if (selectedIndex > 0) {
+ const nextSelected = components[selectedIndex - 1];
+ const nextBreadcrumbs = [...initial(breadcrumbs), nextSelected];
+ dispatch(
+ updateStore({
+ selected: nextSelected,
+ breadcrumbs: nextBreadcrumbs
+ })
+ );
+ }
+ };
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 { pick } from 'lodash';
+import { DISPLAY_HOME } from './../app/actions';
+import { UPDATE_STORE, INIT } from './treeViewActions';
+
+const initialState = {
+ components: [],
+ breadcrumbs: [],
+ total: 0
+};
+
+export default function drilldownReducer(state = initialState, action = {}) {
+ switch (action.type) {
+ case DISPLAY_HOME:
+ return initialState;
+ case UPDATE_STORE:
+ return { ...state, ...action.state };
+ case INIT:
+ return { ...state, ...pick(action, ['rootComponent', 'metric', 'periodIndex']) };
+ default:
+ return state;
+ }
+}
--- /dev/null
+.home-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+ margin-bottom: 20px;
+}
+
+.measures-domains {
+}
+
+.measures-domains > li {
+ margin-bottom: 20px;
+}
+
+.measures-domains > li > div {
+ border: 1px solid #e6e6e6;
+ background-color: #fff;
+}
+
+.measures-domains-leak-header {
+ line-height: 22px;
+ padding: 0 10px;
+ border: 1px solid #eae3c7;
+ background-color: #fbf3d5;
+ white-space: nowrap;
+}
+
+.main-domain-measures {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: space-around;
+ float: left;
+ width: 480px;
+ margin-right: 60px;
+}
+
+.main-domain-measures > li {
+ padding: 12px 0;
+}
+
+.main-domain-measures > li > a {
+ display: flex;
+ flex-direction: column-reverse;
+ width: 160px;
+ border: none;
+ text-align: center;
+}
+
+.main-domain-measures .domain-measures-value {
+ height: 40px;
+ box-sizing: border-box;
+ color: #444;
+ font-size: 30px;
+ font-weight: 300;
+}
+
+.main-domain-measures .domain-measures-value .rating,
+.measure-details-value .rating {
+ vertical-align: top;
+ width: 40px;
+ height: 40px;
+ line-height: 40px;
+ border-radius: 40px;
+ font-size: 24px;
+}
+
+.main-domain-measures .domain-measures-name {
+ margin-top: 8px;
+}
+
+.main-domain-measures .domain-measures-name > span:last-child {
+ border-bottom: 1px solid #cae3f2;
+}
+
+.main-domain-measures .domain-measures-leak {
+ margin: 0 20px;
+ border: 1px solid #eae3c7;
+ background-color: #fbf3d5;
+}
+
+.domain-measures {
+ overflow: hidden;
+ line-height: 1.4;
+}
+
+.domain-measures > li > a {
+ display: flex;
+ justify-content: space-between;
+ border: none;
+}
+
+.domain-measures > li:nth-child(odd) > a {
+ background-color: #f8f8f8;
+}
+
+.domain-measures > li > a:hover {
+ background-color: #ecf6fe !important;
+}
+
+.domain-measures .domain-measures-name,
+.domain-measures .domain-measures-value {
+ padding: 7px 10px;
+ box-sizing: border-box;
+}
+
+.domain-measures .domain-measures-name {
+ width: calc(100% - 160px);
+ line-height: 24px;
+}
+
+.domain-measures .domain-measures-name > span {
+ border-bottom: 1px solid #cae3f2;
+}
+
+.domain-measures .domain-measures-value {
+ min-width: 80px;
+ color: #444;
+ text-align: right;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.domain-measures .domain-measures-leak {
+ background-color: #fbf3d5;
+ transition: background-color 0.3s ease;
+}
+
+.domain-measures .domain-measures > li:nth-child(odd) .domain-measures-leak {
+ background-color: #f5eed0;
+}
+
+.measure-details {
+ margin-top: 10px;
+}
+
+.measure-details-header {
+ position: relative;
+ margin-top: 10px;
+ margin-bottom: 30px;
+}
+
+.measure-details-metric,
+.measure-details-value {
+}
+
+.measure-details-metric {
+ display: inline-block;
+ margin-bottom: 10px;
+}
+
+.measure-details-metric > a:not(.button) {
+ border: none;
+ color: #444;
+}
+
+.measure-details-metric > a:not(.button):hover {
+ color: #236a97;
+}
+
+.measure-details-value {
+ font-size: 24px;
+}
+
+.measure-details-value-absolute {
+ display: inline-block;
+ vertical-align: middle;
+ padding: 5px 0;
+ font-weight: 300;
+}
+
+.measure-details-value-leak {
+ display: inline-block;
+ vertical-align: middle;
+ padding: 4px 10px;
+ border: 1px solid #eae3c7;
+ background-color: #fbf3d5;
+ font-weight: 300;
+}
+
+.measure-details-value-absolute + .measure-details-value-leak {
+ margin-left: 20px;
+}
+
+.measure-details-secondary {
+ display: inline-block;
+ vertical-align: middle;
+ width: 260px;
+ margin-left: 20px;
+}
+
+.measure-details-drilldown {
+ margin-top: 20px;
+}
+
+.measure-details-drilldown-mode {
+ display: flex;
+ margin-bottom: 10px;
+ border-bottom: 1px solid #e6e6e6;
+}
+
+.measure-details-drilldown-mode > li {
+ margin-bottom: -1px;
+}
+
+.measure-details-drilldown-mode > li + li {
+ margin-left: 2px;
+}
+
+.measure-details-drilldown-mode > li > a {
+ display: inline-block;
+ padding: 5px 10px;
+ border-bottom: 2px solid transparent;
+ color: #444;
+}
+
+.measure-details-drilldown-mode > li > a:hover,
+.measure-details-drilldown-mode > li > a.active {
+ border-bottom-color: #4b9fd5;
+}
+
+.measure-details-drilldown-mode > li > a.active .measure-tab-icon path {
+ fill: #4b9fd5;
+}
+
+.measure-details-plain-list {
+}
+
+.measure-details-components {
+ width: 300px;
+ padding: 10px;
+ box-sizing: border-box;
+}
+
+.measure-details-components > li > a {
+ display: flex;
+ padding-top: 5px;
+ padding-bottom: 5px;
+ border: none;
+ color: #444;
+}
+
+.measure-details-components > li > a:hover,
+.measure-details-components > li > a.selected {
+ background-color: #cae3f2 !important;
+}
+
+.measure-details-component-name,
+.measure-details-component-value {
+ padding-right: 10px;
+ box-sizing: border-box;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.measure-details-component-name {
+ width: calc(100% - 60px);
+ padding-left: 10px;
+}
+
+.measure-details-component-value {
+ width: 60px;
+ text-align: right;
+}
+
+.measure-details-viewer {
+ min-height: 100vh;
+}
+
+.measure-details-viewer-header {
+ margin-bottom: 10px;
+ line-height: 24px;
+}
+
+.measure-details-viewer-header .button-group {
+ vertical-align: top;
+}
+
+.measure-details-header-container {
+ display: inline-block;
+ line-height: 16px;
+ padding-right: 10px;
+}
+
+.measure-tab-icon {
+ display: inline-block;
+ width: 14px;
+ height: 14px;
+ margin-right: 6px;
+}
+
+.measure-tab-icon path {
+ fill: #aeaeae;
+ transition: fill 0.3s ease;
+}
+
+.measure-details-components-up-icon path {
+ fill: #aeaeae;
+}
+
+.measure-details-history {
+ padding: 10px 0;
+}
+
+.measure-details-bubble-chart {
+ position: relative;
+ margin: 40px 0 10px;
+ padding: 30px 0 30px 60px;
+ border: 1px solid #e6e6e6;
+ background-color: #fff;
+}
+
+.measure-details-bubble-chart-axis {
+ position: absolute;
+ color: #777;
+ font-size: 12px;
+}
+
+.measure-details-bubble-chart-axis.x {
+ left: 50%;
+ bottom: 10px;
+ width: 500px;
+ margin-left: -250px;
+ text-align: center;
+}
+
+.measure-details-bubble-chart-axis.y {
+ top: 50%;
+ left: -20px;
+ transform: rotate(-90deg);
+}
+
+.measure-details-bubble-chart-axis.size {
+ left: 50%;
+ top: 10px;
+ width: 500px;
+ margin-left: -250px;
+ text-align: center;
+}
+
+.measure-details-treemap {
+ margin: 20px 0;
+}
+
+.measure-details-treemap-legend {
+ margin-bottom: 10px;
+}
+
+.component-measures-breadcrumbs {
+ display: flex;
+ flex-wrap: wrap;
+}
+
+.component-measures-breadcrumbs > li {
+ padding: 5px 5px 3px;
+}
+
+.component-measures-breadcrumbs > li:first-child {
+ padding-left: 0;
+}
+
+.component-measures-breadcrumbs > li::after {
+ position: relative;
+ top: -1px;
+ padding-left: 10px;
+ color: #777;
+ font-size: 11px;
+ content: ">";
+}
+
+.component-measures-breadcrumbs > li:last-child::after {
+ display: none;
+}
+
+.home-measures-list {
+ border: 1px solid #e6e6e6;
+ background-color: #fff;
+}
+
+.nav-pills > ul {
+ display: flex;
+ flex-wrap: wrap;
+}
+
+.nav-pills > ul > li > a {
+ display: inline-block;
+ vertical-align: middle;
+ padding: 3px 10px;
+ border: 1px solid transparent;
+ border-radius: 24px;
+ color: #236a97;
+ transition: none;
+}
+
+.nav-pills > ul > li > a:hover {
+ border-color: #236a97;
+}
+
+.nav-pills > ul > li.active > a,
+.nav-pills > ul > li > a.active {
+ background-color: #236a97;
+ color: #fff;
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 bubbles from './config/bubbles';
+import {
+ formatMeasure,
+ formatMeasureVariation,
+ getRatingTooltip as nextGetRatingTooltip,
+ isDiffMetric
+} from '../../helpers/measures';
+
+export function getLeakValue(measure, periodIndex = 1) {
+ if (!measure) {
+ return null;
+ }
+
+ const period = measure.periods
+ ? measure.periods.find(period => period.index === periodIndex)
+ : null;
+
+ return period ? period.value : null;
+}
+
+export function getSingleMeasureValue(measures) {
+ if (!measures || !measures.length) {
+ return null;
+ }
+
+ return measures[0].value;
+}
+
+export function getSingleLeakValue(measures, periodIndex = 1) {
+ if (!measures || !measures.length) {
+ return null;
+ }
+
+ const measure = measures[0];
+
+ const period = measure.periods
+ ? measure.periods.find(period => period.index === periodIndex)
+ : null;
+
+ return period ? period.value : null;
+}
+
+export function formatLeak(value, metric, options) {
+ if (isDiffMetric(metric.key)) {
+ return formatMeasure(value, metric.type, options);
+ } else {
+ return formatMeasureVariation(value, metric.type, options);
+ }
+}
+
+export function enhanceWithLeak(measures, periodIndex = 1) {
+ function enhanceSingle(measure) {
+ return { ...measure, leak: getLeakValue(measure, periodIndex) };
+ }
+
+ if (Array.isArray(measures)) {
+ return measures.map(enhanceSingle);
+ } else {
+ return enhanceSingle(measures);
+ }
+}
+
+export function enhanceWithSingleMeasure(components, periodIndex = 1) {
+ return components.map(component => {
+ return {
+ ...component,
+ value: getSingleMeasureValue(component.measures),
+ leak: getSingleLeakValue(component.measures, periodIndex)
+ };
+ });
+}
+
+export function enhanceWithMeasure(components, metric, periodIndex = 1) {
+ return components.map(component => {
+ const measuresWithLeak = enhanceWithLeak(component.measures, periodIndex);
+ const measure = measuresWithLeak.find(measure => measure.metric === metric);
+ const value = measure ? measure.value : null;
+ const leak = measure ? measure.leak : null;
+ return { ...component, value, leak, measures: measuresWithLeak };
+ });
+}
+
+export function hasBubbleChart(domainName) {
+ return !!bubbles[domainName];
+}
+
+export function hasTreemap(metric) {
+ return ['PERCENT', 'RATING', 'LEVEL'].indexOf(metric.type) !== -1;
+}
+
+export function filterOutEmptyMeasures(components) {
+ return components.filter(component => component.value !== null || component.leak !== null);
+}
+
+export function getRatingTooltip(metricKey, value) {
+ const finalMetricKey = metricKey.indexOf('new_') === 0 ? metricKey.substr(4) : metricKey;
+ const KNOWN_RATINGS = ['sqale_rating', 'reliability_rating', 'security_rating'];
+ if (KNOWN_RATINGS.includes(finalMetricKey)) {
+ return nextGetRatingTooltip(finalMetricKey, value);
+ }
+ return null;
+}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 Helmet from 'react-helmet';
-import Spinner from './../components/Spinner';
-import { translate } from '../../../helpers/l10n';
-import '../styles.css';
-
-export default class App extends React.PureComponent {
- state = { componentSet: false };
-
- componentDidMount() {
- this.props.setComponent(this.props.component);
- this.props.fetchMetrics();
- this.setState({ componentSet: true });
- }
-
- render() {
- if (this.props.metrics == null || !this.state.componentSet) {
- return <Spinner />;
- }
-
- return (
- <main id="component-measures">
- <Helmet title={translate('layout.measures')} />
- {this.props.children}
- </main>
- );
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 { connect } from 'react-redux';
-import App from './App';
-import { fetchMetrics, setComponent } from './actions';
-import { getComponent, getMeasuresAppAllMetrics } from '../../../store/rootReducer';
-
-const mapStateToProps = (state, ownProps) => ({
- component: getComponent(state, ownProps.location.query.id),
- metrics: getMeasuresAppAllMetrics(state)
-});
-
-const mapDispatchToProps = dispatch => {
- return {
- fetchMetrics: () => dispatch(fetchMetrics()),
- setComponent: component => dispatch(setComponent(component))
- };
-};
-
-export default connect(mapStateToProps, mapDispatchToProps)(App);
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 { getMetrics } from '../../../api/metrics';
-
-/*
- * Actions
- */
-
-export const DISPLAY_HOME = 'measuresApp/app/DISPLAY_HOME';
-export const RECEIVE_METRICS = 'measuresApp/app/RECEIVE_METRICS';
-export const SET_COMPONENT = 'measuresApp/app/SET_COMPONENT';
-
-/*
- * Action Creators
- */
-
-export function displayHome() {
- return { type: DISPLAY_HOME };
-}
-
-function receiveMetrics(metrics) {
- return { type: RECEIVE_METRICS, metrics };
-}
-
-export function setComponent(component) {
- return { type: SET_COMPONENT, component };
-}
-
-/*
- * Workflow
- */
-
-export function fetchMetrics() {
- return dispatch => {
- getMetrics().then(metrics => {
- dispatch(receiveMetrics(metrics));
- });
- };
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 { RECEIVE_METRICS, SET_COMPONENT } from './actions';
-
-const initialState = {
- metrics: undefined
-};
-
-export default function appReducer(state = initialState, action = {}) {
- switch (action.type) {
- case RECEIVE_METRICS:
- return { ...state, metrics: action.metrics };
- case SET_COMPONENT:
- return { ...state, component: action.component };
- default:
- return state;
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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';
-
-export default function IconBubbles() {
- /* eslint max-len: 0 */
- return (
- <svg
- className="measure-tab-icon"
- viewBox="0 0 512 448"
- fillRule="evenodd"
- clipRule="evenodd"
- strokeLinejoin="round"
- strokeMiterlimit="1.414">
- <path d="M352 256c52.984 0 96 43.016 96 96s-43.016 96-96 96-96-43.016-96-96 43.016-96 96-96zM128 96c70.645 0 128 57.355 128 128 0 70.645-57.355 128-128 128C57.355 352 0 294.645 0 224 0 153.355 57.355 96 128 96zM352 0c52.984 0 96 43.016 96 96s-43.016 96-96 96-96-43.016-96-96 43.016-96 96-96z" />
- </svg>
- );
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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';
-
-export default function ListIcon() {
- /* eslint max-len: 0 */
- return (
- <svg
- className="measure-tab-icon"
- viewBox="0 0 448 448"
- fillRule="evenodd"
- clipRule="evenodd"
- strokeLinejoin="round"
- strokeMiterlimit="1.414">
- <path d="M448 48c0-8.83-7.17-16-16-16H16C7.17 32 0 39.17 0 48v32c0 8.83 7.17 16 16 16h416c8.83 0 16-7.17 16-16V48zM448 144c0-8.83-7.17-16-16-16H16c-8.83 0-16 7.17-16 16v32c0 8.83 7.17 16 16 16h416c8.83 0 16-7.17 16-16v-32zM448 240c0-8.83-7.17-16-16-16H16c-8.83 0-16 7.17-16 16v32c0 8.83 7.17 16 16 16h416c8.83 0 16-7.17 16-16v-32zM448 336.03c0-8.83-7.17-16-16-16H16c-8.83 0-16 7.17-16 16v32c0 8.83 7.17 16 16 16h416c8.83 0 16-7.17 16-16v-32z" />
- </svg>
- );
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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';
-
-export default function IconTree() {
- /* eslint max-len: 0 */
- return (
- <svg
- className="measure-tab-icon"
- viewBox="0 0 448 448"
- fillRule="evenodd"
- clipRule="evenodd"
- strokeLinejoin="round"
- strokeMiterlimit="1.414">
- <path d="M448 48c0-8.83-7.17-16-16-16H16C7.17 32 0 39.17 0 48v32c0 8.83 7.17 16 16 16h416c8.83 0 16-7.17 16-16V48zM448 144c0-8.83-6.146-16-13.714-16H77.714C70.144 128 64 135.17 64 144v32c0 8.83 6.145 16 13.714 16h356.572c7.568 0 13.714-7.17 13.714-16v-32zM448 240c0-8.83-5.12-16-11.428-16H139.428C133.12 224 128 231.17 128 240v32c0 8.83 5.12 16 11.428 16h297.144c6.307 0 11.428-7.17 11.428-16v-32zM448 336.03c0-8.83-4.097-16-9.142-16H201.143c-5.046 0-9.143 7.17-9.143 16v32c0 8.83 4.097 16 9.143 16h237.715c5.045 0 9.142-7.17 9.142-16v-32z" />
- </svg>
- );
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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';
-
-export default function IconTreemap() {
- return (
- <svg
- className="measure-tab-icon"
- viewBox="0 0 448 448"
- fillRule="evenodd"
- clipRule="evenodd"
- strokeLinejoin="round"
- strokeMiterlimit="1.414">
- <path d="M0 0h224v448H0zM256 0h192v256H256zM256 288h192v160H256z" />
- </svg>
- );
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 moment from 'moment';
-import Tooltip from '../../../components/controls/Tooltip';
-import { getPeriodLabel, getPeriodDate } from '../../../helpers/periods';
-import { translate, translateWithParameters } from '../../../helpers/l10n';
-
-export default function LeakPeriodLegend({ component, period }) {
- if (component.qualifier === 'APP') {
- return (
- <div className="measures-domains-leak-header">
- {translate('issues.leak_period')}
- </div>
- );
- }
-
- const label = (
- <div className="measures-domains-leak-header">
- {translateWithParameters('overview.leak_period_x', getPeriodLabel(period))}
- </div>
- );
-
- if (period.mode === 'days') {
- return label;
- }
-
- const date = getPeriodDate(period);
- const fromNow = moment(date).fromNow();
- const tooltip = fromNow + ', ' + moment(date).format('LL');
- return (
- <Tooltip placement="bottom" overlay={tooltip}>
- {label}
- </Tooltip>
- );
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 PropTypes from 'prop-types';
-import Rating from '../../../components/ui/Rating';
-import Level from '../../../components/ui/Level';
-import { formatMeasure, isDiffMetric } from '../../../helpers/measures';
-import { TooltipsContainer } from '../../../components/mixins/tooltips-mixin';
-import { formatLeak, getRatingTooltip } from '../utils';
-
-export default class Measure extends React.PureComponent {
- static propTypes = {
- className: PropTypes.string,
- measure: PropTypes.object,
- metric: PropTypes.object,
- decimals: PropTypes.number
- };
-
- renderRating(measure, metric) {
- const value = isDiffMetric(metric.key) ? measure.leak : measure.value;
- const tooltip = getRatingTooltip(metric.key, value);
- const rating = <Rating value={value} />;
-
- if (tooltip) {
- return (
- <TooltipsContainer>
- <span>
- <span title={tooltip} data-toggle="tooltip">
- {rating}
- </span>
- </span>
- </TooltipsContainer>
- );
- }
-
- return rating;
- }
-
- render() {
- const { measure, metric, decimals, className } = this.props;
- const finalMetric = metric || measure.metric;
-
- if (finalMetric.type === 'RATING') {
- return this.renderRating(measure, finalMetric);
- }
-
- if (finalMetric.type === 'LEVEL') {
- return <Level level={measure.value} />;
- }
-
- const formattedValue = isDiffMetric(finalMetric.key)
- ? formatLeak(measure.leak, finalMetric, { decimals })
- : formatMeasure(measure.value, finalMetric.type, { decimals });
- return (
- <span className={className}>
- {formattedValue != null ? formattedValue : '–'}
- </span>
- );
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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';
-
-export default function Spinner() {
- return <i className="spinner spinner-margin" />;
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 Spinner from './../Spinner';
-import OriginalBubbleChart from '../../../../components/charts/BubbleChart';
-import bubbles from '../../config/bubbles';
-import { getComponentLeaves } from '../../../../api/components';
-import { formatMeasure } from '../../../../helpers/measures';
-import Workspace from '../../../../components/workspace/main';
-import { getComponentUrl } from '../../../../helpers/urls';
-import { getLocalizedMetricName, translateWithParameters } from '../../../../helpers/l10n';
-
-const HEIGHT = 500;
-const BUBBLES_LIMIT = 500;
-
-function getMeasure(component, metric) {
- return Number(component.measures[metric]) || 0;
-}
-
-export default class BubbleChart extends React.PureComponent {
- state = {
- fetching: 0,
- files: []
- };
-
- componentWillMount() {
- this.updateMetrics(this.props);
- }
-
- componentDidMount() {
- this.mounted = true;
- this.fetchFiles();
- }
-
- componentWillUpdate(nextProps) {
- this.updateMetrics(nextProps);
- }
-
- componentDidUpdate(nextProps) {
- if (nextProps.domainName !== this.props.domainName) {
- this.fetchFiles();
- }
- }
-
- componentWillUnmount() {
- this.mounted = false;
- }
-
- updateMetrics(props) {
- const { metrics, domainName } = props;
- const conf = bubbles[domainName];
- this.xMetric = metrics.find(m => m.key === conf.x);
- this.yMetric = metrics.find(m => m.key === conf.y);
- this.sizeMetric = metrics.find(m => m.key === conf.size);
- }
-
- fetchFiles() {
- const { component } = this.props;
- const metrics = [this.xMetric.key, this.yMetric.key, this.sizeMetric.key];
- const options = {
- s: 'metric',
- metricSort: this.sizeMetric.key,
- asc: false,
- ps: BUBBLES_LIMIT
- };
-
- if (this.mounted) {
- this.setState({ fetching: this.state.fetching + 1 });
- }
-
- getComponentLeaves(component.key, metrics, options).then(r => {
- const files = r.components.map(file => {
- const measures = {};
-
- file.measures.forEach(measure => {
- measures[measure.metric] = measure.value;
- });
- return { ...file, measures };
- });
-
- if (this.mounted) {
- this.setState({
- files,
- fetching: this.state.fetching - 1,
- total: files.length
- });
- }
- });
- }
-
- getTooltip(component) {
- const x = formatMeasure(getMeasure(component, this.xMetric.key), this.xMetric.type);
- const y = formatMeasure(getMeasure(component, this.yMetric.key), this.yMetric.type);
- const size = formatMeasure(getMeasure(component, this.sizeMetric.key), this.sizeMetric.type);
- const inner = [
- component.name,
- `${this.xMetric.name}: ${x}`,
- `${this.yMetric.name}: ${y}`,
- `${this.sizeMetric.name}: ${size}`
- ].join('<br>');
-
- return `<div class="text-left">${inner}</div>`;
- }
-
- handleBubbleClick(component) {
- if (['FIL', 'UTS'].includes(component.qualifier)) {
- Workspace.openComponent({ key: component.key });
- } else {
- window.location = getComponentUrl(component.refKey || component.key);
- }
- }
-
- renderBubbleChart() {
- const items = this.state.files.map(file => {
- return {
- x: getMeasure(file, this.xMetric.key),
- y: getMeasure(file, this.yMetric.key),
- size: getMeasure(file, this.sizeMetric.key),
- link: file,
- tooltip: this.getTooltip(file)
- };
- });
-
- const formatXTick = tick => formatMeasure(tick, this.xMetric.type);
- const formatYTick = tick => formatMeasure(tick, this.yMetric.type);
-
- return (
- <OriginalBubbleChart
- items={items}
- height={HEIGHT}
- padding={[25, 60, 50, 60]}
- formatXTick={formatXTick}
- formatYTick={formatYTick}
- onBubbleClick={this.handleBubbleClick.bind(this)}
- />
- );
- }
-
- render() {
- const { fetching } = this.state;
-
- if (fetching) {
- return (
- <div className="measure-details-bubble-chart">
- <div className="note text-center" style={{ lineHeight: `${HEIGHT}px` }}>
- <Spinner />
- </div>
- </div>
- );
- }
-
- return (
- <div className="measure-details-bubble-chart">
- <div>
- {this.renderBubbleChart()}
- </div>
-
- <div className="measure-details-bubble-chart-axis x">
- {getLocalizedMetricName(this.xMetric)}
- </div>
- <div className="measure-details-bubble-chart-axis y">
- {getLocalizedMetricName(this.yMetric)}
- </div>
- <div className="measure-details-bubble-chart-axis size">
- {translateWithParameters(
- 'component_measures.legend.size_x',
- getLocalizedMetricName(this.sizeMetric)
- )}
- </div>
- </div>
- );
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 { connect } from 'react-redux';
-import MeasureBubbleChart from './BubbleChart';
-import { getMeasuresAppAllMetrics, getMeasuresAppComponent } from '../../../../store/rootReducer';
-
-const mapStateToProps = state => {
- return {
- component: getMeasuresAppComponent(state),
- metrics: getMeasuresAppAllMetrics(state)
- };
-};
-
-const mapDispatchToProps = () => {
- return {};
-};
-
-export default connect(mapStateToProps, mapDispatchToProps)(MeasureBubbleChart);
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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.
- */
-const bubblesConfig = {
- Reliability: { x: 'ncloc', y: 'reliability_remediation_effort', size: 'bugs' },
- Security: { x: 'ncloc', y: 'security_remediation_effort', size: 'vulnerabilities' },
- Maintainability: { x: 'ncloc', y: 'sqale_index', size: 'code_smells' },
- Coverage: { x: 'complexity', y: 'coverage', size: 'uncovered_lines' },
- Duplications: { x: 'ncloc', y: 'duplicated_lines', size: 'duplicated_blocks' }
-};
-
-export default bubblesConfig;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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.
- */
-export default {
- coverage: ['uncovered_lines', 'uncovered_conditions'],
- line_coverage: ['uncovered_lines'],
- branch_coverage: ['uncovered_conditions'],
- uncovered_lines: ['line_coverage'],
- uncovered_conditions: ['branch_coverage'],
-
- new_coverage: ['new_uncovered_lines', 'new_uncovered_conditions'],
- new_line_coverage: ['new_uncovered_lines'],
- new_branch_coverage: ['new_uncovered_conditions'],
- new_uncovered_lines: ['new_line_coverage'],
- new_uncovered_conditions: ['new_branch_coverage'],
-
- duplicated_lines_density: ['duplicated_lines'],
- new_duplicated_lines_density: ['new_duplicated_lines'],
- duplicated_lines: ['duplicated_lines_density'],
- new_duplicated_lines: ['new_duplicated_lines_density']
-};
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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.
- */
-export const domains = {
- Reliability: {
- main: ['bugs', 'new_bugs', 'reliability_rating'],
- order: [
- 'bugs',
- 'new_bugs',
- 'reliability_rating',
- 'reliability_remediation_effort',
- 'new_reliability_remediation_effort'
- ]
- },
-
- Security: {
- main: ['vulnerabilities', 'new_vulnerabilities', 'security_rating'],
- order: [
- 'vulnerabilities',
- 'new_vulnerabilities',
- 'security_rating',
- 'security_remediation_effort',
- 'new_security_remediation_effort'
- ]
- },
-
- Maintainability: {
- main: ['code_smells', 'new_code_smells', 'sqale_rating'],
- order: [
- 'code_smells',
- 'new_code_smells',
- 'sqale_rating',
- 'sqale_index',
- 'new_technical_debt',
- 'sqale_debt_ratio',
- 'new_sqale_debt_ratio',
- 'effort_to_reach_maintainability_rating_a'
- ]
- },
-
- Coverage: {
- main: ['coverage', 'new_coverage', 'tests'],
- order: [
- 'coverage',
- 'new_coverage',
- 'line_coverage',
- 'new_line_coverage',
- 'branch_coverage',
- 'new_branch_coverage',
- 'uncovered_lines',
- 'new_uncovered_lines',
- 'uncovered_conditions',
- 'new_uncovered_conditions',
- 'new_lines_to_cover',
-
- 'lines_to_cover',
-
- 'tests',
- 'test_success',
- 'test_errors',
- 'test_failures',
- 'skipped_tests',
- 'test_success_density',
- 'test_execution_time'
- ]
- },
-
- Duplications: {
- main: ['duplicated_lines_density', 'new_duplicated_lines_density'],
- order: [
- 'duplicated_lines_density',
- 'new_duplicated_lines_density',
- 'duplicated_blocks',
- 'new_duplicated_blocks',
- 'duplicated_lines',
- 'new_duplicated_lines',
- 'duplicated_files'
- ]
- },
-
- Size: {
- main: ['ncloc'],
- order: [
- 'ncloc',
- 'lines',
- 'new_lines',
- 'statements',
- 'functions',
- 'classes',
- 'files',
- 'directories'
- ]
- },
-
- Complexity: {
- main: ['complexity'],
- order: ['complexity', 'function_complexity', 'file_complexity', 'class_complexity']
- },
-
- Releasability: {
- main: ['alert_status', 'releasability_rating'],
- order: ['alert_status']
- },
-
- Issues: {
- main: ['violations', 'new_violations'],
- order: [
- 'violations',
- 'new_violations',
- 'blocker_violations',
- 'new_blocker_violations',
- 'critical_violations',
- 'new_critical_violations',
- 'major_violations',
- 'new_major_violations',
- 'minor_violations',
- 'new_minor_violations',
- 'info_violations',
- 'new_info_violations',
- 'open_issues',
- 'reopened_issues',
- 'confirmed_issues',
- 'false_positive_issues'
- ]
- }
-};
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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, IndexLink } from 'react-router';
-import Spinner from './../components/Spinner';
-import MeasureDetailsHeader from './MeasureDetailsHeader';
-import MeasureDrilldown from './drilldown/MeasureDrilldown';
-import MetricNotFound from './MetricNotFound';
-import { getPeriod, getPeriodDate } from '../../../helpers/periods';
-import { translate, translateWithParameters } from '../../../helpers/l10n';
-
-export default class MeasureDetails extends React.PureComponent {
- mounted: boolean;
-
- state = {
- loading: true
- };
-
- componentDidMount() {
- this.mounted = true;
- this.loadData();
- }
-
- componentDidUpdate(prevProps) {
- if (prevProps.params.metricKey !== this.props.params.metricKey) {
- this.loadData();
- }
- }
-
- componentWillUnmount() {
- this.mounted = false;
- }
-
- metricExists(): boolean {
- const { metrics } = this.props;
- const { metricKey } = this.props.params;
- const metric = metrics.find(metric => metric.key === metricKey);
- return !!metric;
- }
-
- loadData() {
- if (this.metricExists()) {
- this.setState({ loading: true });
- const periodIndex = this.props.location.query.period || 1;
- const onLoaded = () => this.mounted && this.setState({ loading: false });
- this.props
- .fetchMeasure(this.props.params.metricKey, Number(periodIndex))
- .then(onLoaded, onLoaded);
- }
- }
-
- render() {
- if (!this.metricExists()) {
- return <MetricNotFound />;
- }
-
- if (this.state.loading) {
- return <Spinner />;
- }
-
- const { component, metric, secondaryMeasure, measure, periods, children } = this.props;
-
- if (!measure) {
- return <MetricNotFound />;
- }
-
- const { tab } = this.props.params;
- const periodIndex = this.props.location.query.period || 1;
- const period = getPeriod(periods, Number(periodIndex));
- const periodDate = getPeriodDate(period);
-
- return (
- <section id="component-measures-details" className="page page-container page-limited">
- <div className="note">
- <IndexLink
- to={{ pathname: '/component_measures', query: { id: component.key } }}
- id="component-measures-back-to-all-measures"
- className="text-muted">
- {translate('component_measures.all_measures')}
- </IndexLink>
- {!!metric.domain &&
- <span>
- {' / '}
- <Link
- to={{
- pathname: `/component_measures/domain/${metric.domain}`,
- query: { id: component.key }
- }}
- className="text-muted">
- {translateWithParameters('component_measures.domain_measures', metric.domain)}
- </Link>
- </span>}
- </div>
-
- <MeasureDetailsHeader
- component={component}
- leakPeriod={period}
- measure={measure}
- metric={metric}
- secondaryMeasure={secondaryMeasure}
- />
-
- {measure &&
- <MeasureDrilldown
- component={component}
- metric={metric}
- tab={tab}
- leakPeriod={period}
- leakPeriodDate={periodDate}>
- {children}
- </MeasureDrilldown>}
- </section>
- );
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 { connect } from 'react-redux';
-import MeasureDetails from './MeasureDetails';
-import { fetchMeasure } from './actions';
-import {
- getMeasuresAppAllMetrics,
- getMeasuresAppDetailsMetric,
- getMeasuresAppDetailsMeasure,
- getMeasuresAppDetailsSecondaryMeasure,
- getMeasuresAppDetailsPeriods,
- getMeasuresAppComponent
-} from '../../../store/rootReducer';
-
-const mapStateToProps = state => {
- return {
- component: getMeasuresAppComponent(state),
- metrics: getMeasuresAppAllMetrics(state),
- metric: getMeasuresAppDetailsMetric(state),
- measure: getMeasuresAppDetailsMeasure(state),
- secondaryMeasure: getMeasuresAppDetailsSecondaryMeasure(state),
- periods: getMeasuresAppDetailsPeriods(state)
- };
-};
-
-const mapDispatchToProps = { fetchMeasure };
-
-export default connect(mapStateToProps, mapDispatchToProps)(MeasureDetails);
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 Measure from './../components/Measure';
-import LanguageDistribution from '../../../components/charts/LanguageDistribution';
-import LeakPeriodLegend from '../components/LeakPeriodLegend';
-import IssueTypeIcon from '../../../components/ui/IssueTypeIcon';
-import HistoryIcon from '../../../components/icons-components/HistoryIcon';
-import Tooltip from '../../../components/controls/Tooltip';
-import { ComplexityDistribution } from '../../../components/shared/complexity-distribution';
-import { isDiffMetric } from '../../../helpers/measures';
-import { TooltipsContainer } from '../../../components/mixins/tooltips-mixin';
-import { getComponentMeasureHistory } from '../../../helpers/urls';
-import { getLocalizedMetricName, translate } from '../../../helpers/l10n';
-
-export default function MeasureDetailsHeader({
- component,
- measure,
- metric,
- secondaryMeasure,
- leakPeriod
-}) {
- const isDiff = isDiffMetric(metric.key);
- return (
- <header className="measure-details-header">
- <h2 className="measure-details-metric">
- <IssueTypeIcon query={metric.key} className="little-spacer-right" />
- {getLocalizedMetricName(metric)}
- {!isDiff &&
- <Tooltip placement="right" overlay={translate('component_measures.show_metric_history')}>
- <Link
- className="js-show-history spacer-left button button-small button-compact"
- to={getComponentMeasureHistory(component.key, metric.key)}>
- <HistoryIcon />
- </Link>
- </Tooltip>}
- </h2>
-
- {isDiff &&
- <div className="pull-right">
- <LeakPeriodLegend component={component} period={leakPeriod} />
- </div>}
-
- <TooltipsContainer options={{ html: false }}>
- <div className="measure-details-value">
- {isDiff
- ? <div className="measure-details-value-leak">
- <Measure measure={measure} metric={metric} />
- </div>
- : <div className="measure-details-value-absolute">
- <Measure measure={measure} metric={metric} />
- </div>}
-
- {secondaryMeasure &&
- secondaryMeasure.metric === 'ncloc_language_distribution' &&
- <div className="measure-details-secondary">
- <LanguageDistribution distribution={secondaryMeasure.value} />
- </div>}
-
- {secondaryMeasure &&
- secondaryMeasure.metric === 'function_complexity_distribution' &&
- <div className="measure-details-secondary">
- <ComplexityDistribution distribution={secondaryMeasure.value} of="function" />
- </div>}
-
- {secondaryMeasure &&
- secondaryMeasure.metric === 'file_complexity_distribution' &&
- <div className="measure-details-secondary">
- <ComplexityDistribution distribution={secondaryMeasure.value} of="file" />
- </div>}
- </div>
- </TooltipsContainer>
- </header>
- );
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 { translate } from '../../../helpers/l10n';
-
-export default function MetricNotFound() {
- return (
- <div className="page page-limited">
- <div className="alert alert-danger">
- {translate('component_measures.not_found')}
- </div>
- </div>
- );
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 { getMeasuresAndMeta } from '../../../api/measures';
-import { enhanceWithLeak } from '../utils';
-import { getMeasuresAppComponent, getMeasuresAppAllMetrics } from '../../../store/rootReducer';
-
-/*
- * Actions
- */
-
-export const REQUEST_MEASURE = 'measuresApp/details/REQUEST_MEASURE';
-export const RECEIVE_MEASURE = 'measuresApp/details/RECEIVE_MEASURE';
-
-/*
- * Action Creators
- */
-
-function requestMeasure(metric) {
- return { type: REQUEST_MEASURE, metric };
-}
-
-function receiveMeasure(measure, secondaryMeasure, periods) {
- return { type: RECEIVE_MEASURE, measure, secondaryMeasure, periods };
-}
-
-/*
- * Workflow
- */
-
-export function fetchMeasure(metricKey, periodIndex = 1) {
- return (dispatch, getState) => {
- const state = getState();
- const component = getMeasuresAppComponent(state);
- const metrics = getMeasuresAppAllMetrics(state);
- const metricsToRequest = [metricKey];
-
- if (metricKey === 'ncloc') {
- metricsToRequest.push('ncloc_language_distribution');
- }
- if (metricKey === 'function_complexity') {
- metricsToRequest.push('function_complexity_distribution');
- }
- if (metricKey === 'file_complexity') {
- metricsToRequest.push('file_complexity_distribution');
- }
-
- const metric = metrics.find(m => m.key === metricKey);
- dispatch(requestMeasure(metric));
-
- return getMeasuresAndMeta(component.key, metricsToRequest, {
- additionalFields: 'periods'
- }).then(r => {
- const measures = enhanceWithLeak(r.component.measures, periodIndex);
- const measure = measures.find(m => m.metric === metricKey);
- const secondaryMeasure = measures.find(m => m.metric !== metricKey);
- dispatch(receiveMeasure(measure, secondaryMeasure, r.periods));
- });
- };
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 QualifierIcon from '../../../../components/shared/QualifierIcon';
-import { formatLeak } from '../../utils';
-import { formatMeasure, isDiffMetric } from '../../../../helpers/measures';
-
-const Breadcrumb = ({ component, metric, onBrowse }) => {
- const handleClick = e => {
- e.preventDefault();
- e.target.blur();
- onBrowse(component);
- };
-
- let inner;
- if (onBrowse) {
- inner = (
- <a id={'component-measures-breadcrumb-' + component.key} href="#" onClick={handleClick}>
- {component.name}
- </a>
- );
- } else {
- inner = (
- <span>
- {component.name}
- </span>
- );
- }
-
- const value = isDiffMetric(metric.key)
- ? formatLeak(component.leak, metric)
- : formatMeasure(component.value, metric.type);
-
- return (
- <span>
- <QualifierIcon qualifier={component.qualifier} />
-
- {inner}
- {value != null &&
- <span>
- {' (' + value + ')'}
- </span>}
- </span>
- );
-};
-
-export default Breadcrumb;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 Breadcrumb from './Breadcrumb';
-
-const Breadcrumbs = ({ breadcrumbs, metric, onBrowse }) =>
- <ul className="component-measures-breadcrumbs">
- {breadcrumbs.map((component, index) =>
- <li key={component.key}>
- <Breadcrumb
- component={component}
- metric={metric}
- onBrowse={index + 1 < breadcrumbs.length ? onBrowse : null}
- />
- </li>
- )}
- </ul>;
-
-export default Breadcrumbs;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 classNames from 'classnames';
-import QualifierIcon from '../../../../components/shared/QualifierIcon';
-import { splitPath } from '../../../../helpers/path';
-import { getComponentUrl } from '../../../../helpers/urls';
-
-const ComponentCell = ({ component, isSelected, onClick }) => {
- const linkClassName = classNames('link-no-underline', {
- selected: isSelected
- });
-
- const handleClick = e => {
- const isLeftClickEvent = e.button === 0;
- const isModifiedEvent = !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey);
-
- if (isLeftClickEvent && !isModifiedEvent) {
- e.preventDefault();
- onClick();
- }
- };
-
- let head = '';
- let tail = component.name;
-
- if (['DIR', 'FIL', 'UTS'].includes(component.qualifier)) {
- const parts = splitPath(component.path);
- head = parts.head;
- tail = parts.tail;
- }
-
- const inner = (
- <span title={component.refKey || component.key}>
- <QualifierIcon qualifier={component.qualifier} />
-
- {head.length > 0 &&
- <span className="note">
- {head}/
- </span>}
- <span>{tail}</span>
- </span>
- );
-
- return (
- <td style={{ maxWidth: 0 }}>
- <div
- style={{
- maxWidth: '100%',
- whiteSpace: 'nowrap',
- overflow: 'hidden',
- textOverflow: 'ellipsis'
- }}>
- {component.refId == null || component.qualifier === 'DEV_PRJ'
- ? <a
- id={'component-measures-component-link-' + component.key}
- className={linkClassName}
- href={getComponentUrl(component.key)}
- onClick={handleClick}>
- {inner}
- </a>
- : <a
- id={'component-measures-component-link-' + component.key}
- className={linkClassName}
- href={getComponentUrl(component.refKey || component.key)}>
- <span className="big-spacer-right">
- <i className="icon-detach" />
- </span>
-
- {inner}
- </a>}
- </div>
- </td>
- );
-};
-
-export default ComponentCell;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 ComponentsListRow from './ComponentsListRow';
-import EmptyComponentsList from './EmptyComponentsList';
-import complementary from '../../config/complementary';
-import { getLocalizedMetricName } from '../../../../helpers/l10n';
-
-const ComponentsList = ({ components, metrics, selected, metric, onClick }) => {
- if (!components.length) {
- return <EmptyComponentsList />;
- }
-
- const otherMetrics = (complementary[metric.key] || []).map(metric => {
- return metrics.find(m => m.key === metric);
- });
-
- return (
- <table className="data zebra zebra-hover">
- {otherMetrics.length > 0 &&
- <thead>
- <tr>
- <th> </th>
- <th className="text-right">
- <span className="small">
- {getLocalizedMetricName(metric)}
- </span>
- </th>
- {otherMetrics.map(metric =>
- <th key={metric.key} className="text-right">
- <span className="small">
- {getLocalizedMetricName(metric)}
- </span>
- </th>
- )}
- </tr>
- </thead>}
-
- <tbody>
- {components.map(component =>
- <ComponentsListRow
- key={component.id}
- component={component}
- otherMetrics={otherMetrics}
- isSelected={component === selected}
- metric={metric}
- onClick={onClick}
- />
- )}
- </tbody>
- </table>
- );
-};
-
-export default ComponentsList;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 ComponentCell from './ComponentCell';
-import MeasureCell from './MeasureCell';
-
-const replaceMeasure = (component, measure) => {
- return {
- ...component,
- value: measure.value,
- leak: measure.leak
- };
-};
-
-const ComponentsListRow = ({ component, otherMetrics, isSelected, metric, onClick }) => {
- const handleClick = () => {
- onClick(component);
- };
-
- const otherMeasures = otherMetrics.map(metric => {
- const measure = component.measures.find(measure => measure.metric === metric.key);
- return { ...measure, metric };
- });
-
- return (
- <tr>
- <ComponentCell
- component={component}
- isSelected={isSelected}
- onClick={handleClick.bind(this, component)}
- />
-
- <MeasureCell component={component} metric={metric} />
-
- {otherMeasures.map(measure =>
- <MeasureCell
- key={measure.metric.key}
- component={replaceMeasure(component, measure)}
- metric={measure.metric}
- />
- )}
- </tr>
- );
-};
-
-export default ComponentsListRow;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 { translate } from '../../../../helpers/l10n';
-
-const EmptyComponentsList = () => {
- return (
- <div className="note">
- {translate('no_results')}
- </div>
- );
-};
-
-export default EmptyComponentsList;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 Breadcrumbs from './Breadcrumbs';
-import { translateWithParameters } from '../../../../helpers/l10n';
-
-const ListHeader = props => {
- const { metric, breadcrumbs, onBrowse } = props;
- const { selectedIndex, componentsCount, onSelectPrevious, onSelectNext } = props;
- const hasPrevious = selectedIndex > 0;
- const hasNext = selectedIndex < componentsCount - 1;
- const blur = fn => {
- return e => {
- e.target.blur();
- fn();
- };
- };
-
- return (
- <header className="measure-details-viewer-header">
- {breadcrumbs != null &&
- breadcrumbs.length > 1 &&
- <div className="measure-details-header-container">
- <Breadcrumbs breadcrumbs={breadcrumbs} metric={metric} onBrowse={onBrowse} />
- </div>}
-
- {selectedIndex != null &&
- selectedIndex !== -1 &&
- <div className="pull-right">
- <span className="note spacer-right">
- {translateWithParameters(
- 'component_measures.x_of_y',
- selectedIndex + 1,
- componentsCount
- )}
- </span>
-
- <div className="button-group">
- {hasPrevious && <button onClick={blur(onSelectPrevious)}><</button>}
- {hasNext && <button onClick={blur(onSelectNext)}>></button>}
- </div>
- </div>}
- </header>
- );
-};
-
-export default ListHeader;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 PropTypes from 'prop-types';
-import classNames from 'classnames';
-import moment from 'moment';
-import ComponentsList from './ComponentsList';
-import ListHeader from './ListHeader';
-import Spinner from '../../components/Spinner';
-import SourceViewer from '../../../../components/SourceViewer/SourceViewer';
-import ListFooter from '../../../../components/controls/ListFooter';
-
-export default class ListView extends React.PureComponent {
- static contextTypes = {
- router: PropTypes.object.isRequired
- };
-
- componentDidMount() {
- const { component, metric } = this.props;
- if (component.qualifier === 'DEV') {
- const { router } = this.context;
- router.replace({ pathname: `metric/${metric.key}/tree`, query: { id: component.key } });
- }
- this.handleChangeBaseComponent(component);
- }
-
- componentDidUpdate(nextProps) {
- if (nextProps.metric !== this.props.metric) {
- this.handleChangeBaseComponent(this.props.component);
- }
-
- if (this.props.selected) {
- this.scrollToViewer();
- } else if (this.scrollTop) {
- this.scrollToStoredPosition();
- }
- }
-
- scrollToViewer() {
- const { container } = this.refs;
- const top = container.getBoundingClientRect().top + window.scrollY - 95 - 10;
-
- // scroll only to top
- if (window.scrollY > top) {
- window.scrollTo(0, top);
- }
- }
-
- scrollToStoredPosition() {
- window.scrollTo(0, this.scrollTop);
- this.scrollTop = null;
- }
-
- storeScrollPosition() {
- this.scrollTop = window.scrollY;
- }
-
- handleChangeBaseComponent(baseComponent) {
- const { metric, onFetchList } = this.props;
- const periodIndex = this.props.location.query.period || 1;
- onFetchList(baseComponent, metric, Number(periodIndex));
- }
-
- handleFetchMore() {
- const periodIndex = this.props.location.query.period || 1;
- this.props.onFetchMore(Number(periodIndex));
- }
-
- changeSelected(selected) {
- this.props.onSelect(selected);
- }
-
- handleClick(selected) {
- this.storeScrollPosition();
- this.props.onSelect(selected);
- }
-
- handleBreadcrumbClick() {
- this.props.onSelect(undefined);
- }
-
- render() {
- const {
- component,
- components,
- metrics,
- metric,
- leakPeriod,
- selected,
- total,
- fetching
- } = this.props;
- const { onSelectNext, onSelectPrevious } = this.props;
-
- const breadcrumbs = [component];
- if (selected) {
- breadcrumbs.push(selected);
- }
- const selectedIndex = components.indexOf(selected);
- const sourceViewerPeriod = metric.key.indexOf('new_') === 0 && !!leakPeriod ? leakPeriod : null;
- const sourceViewerPeriodDate =
- sourceViewerPeriod != null ? moment(sourceViewerPeriod.date).toDate() : null;
-
- const filterLine =
- sourceViewerPeriodDate != null
- ? line => {
- if (line.scmDate) {
- const scmDate = moment(line.scmDate).toDate();
- return scmDate >= sourceViewerPeriodDate;
- } else {
- return false;
- }
- }
- : undefined;
-
- return (
- <div ref="container" className="measure-details-plain-list">
- <ListHeader
- metric={metric}
- breadcrumbs={breadcrumbs}
- componentsCount={components.length}
- selectedIndex={selectedIndex}
- onSelectPrevious={onSelectPrevious}
- onSelectNext={onSelectNext}
- onBrowse={this.handleBreadcrumbClick.bind(this)}
- />
-
- {!selected &&
- <div className={classNames({ 'new-loading': fetching })}>
- {!fetching || components.length !== 0
- ? <div>
- <ComponentsList
- components={components}
- metrics={metrics}
- selected={selected}
- metric={metric}
- onClick={this.handleClick.bind(this)}
- />
- <ListFooter
- count={components.length}
- total={total}
- loadMore={this.handleFetchMore.bind(this)}
- />
- </div>
- : <Spinner />}
- </div>}
-
- {!!selected &&
- <div className="measure-details-viewer">
- <SourceViewer component={selected.key} filterLine={filterLine} />
- </div>}
- </div>
- );
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 { connect } from 'react-redux';
-import ListView from './ListView';
-import {
- fetchList,
- fetchMore,
- selectComponent,
- selectNext,
- selectPrevious
-} from '../../store/listViewActions';
-import {
- getMeasuresAppListComponents,
- getMeasuresAppListSelected,
- getMeasuresAppListTotal,
- getMeasuresAppListPageIndex,
- getMeasuresAppAllMetrics,
- getMeasuresAppDetailsMetric,
- isMeasuresAppFetching,
- getMeasuresAppComponent
-} from '../../../../store/rootReducer';
-
-const mapStateToProps = state => {
- return {
- components: getMeasuresAppListComponents(state),
- selected: getMeasuresAppListSelected(state),
- total: getMeasuresAppListTotal(state),
- pageIndex: getMeasuresAppListPageIndex(state),
- component: getMeasuresAppComponent(state),
- metrics: getMeasuresAppAllMetrics(state),
- metric: getMeasuresAppDetailsMetric(state),
- fetching: isMeasuresAppFetching(state)
- };
-};
-
-const mapDispatchToProps = dispatch => {
- return {
- onFetchList: (baseComponent, metric, periodIndex) =>
- dispatch(fetchList(baseComponent, metric, periodIndex)),
- onFetchMore: periodIndex => dispatch(fetchMore(periodIndex)),
- onSelect: component => dispatch(selectComponent(component)),
- onSelectNext: component => dispatch(selectNext(component)),
- onSelectPrevious: component => dispatch(selectPrevious(component))
- };
-};
-
-export default connect(mapStateToProps, mapDispatchToProps)(ListView);
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 Measure from '../../components/Measure';
-
-const MeasureCell = ({ component, metric }) => {
- return (
- <td className="thin nowrap text-right">
- <span id={'component-measures-component-measure-' + component.key + '-' + metric.key}>
- <Measure measure={{ value: component.value, leak: component.leak }} metric={metric} />
- </span>
- </td>
- );
-};
-
-export default MeasureCell;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 IconList from './../../components/IconList';
-import IconTree from './../../components/IconTree';
-import IconBubbles from './../../components/IconBubbles';
-import IconTreemap from './../../components/IconTreemap';
-import { hasBubbleChart, hasTreemap } from '../../utils';
-import { translate } from '../../../../helpers/l10n';
-
-export default function MeasureDrilldown(props) {
- const { children, component, metric, ...other } = props;
-
- const child = React.cloneElement(children, { ...other });
-
- return (
- <div className="measure-details-drilldown">
- <ul className="measure-details-drilldown-mode">
- {component.qualifier !== 'DEV' &&
- <li>
- <Link
- activeClassName="active"
- to={{
- pathname: `/component_measures/metric/${metric.key}/list`,
- query: { id: component.key }
- }}>
- <IconList />
- {translate('component_measures.tab.list')}
- </Link>
- </li>}
-
- <li>
- <Link
- activeClassName="active"
- to={{
- pathname: `/component_measures/metric/${metric.key}/tree`,
- query: { id: component.key }
- }}>
- <IconTree />
- {translate('component_measures.tab.tree')}
- </Link>
- </li>
-
- {hasBubbleChart(metric.key) &&
- <li>
- <Link
- activeClassName="active"
- to={{
- pathname: `/component_measures/metric/${metric.key}/bubbles`,
- query: { id: component.key }
- }}>
- <IconBubbles />
- {translate('component_measures.tab.bubbles')}
- </Link>
- </li>}
-
- {hasTreemap(metric) &&
- <li>
- <Link
- activeClassName="active"
- to={{
- pathname: `/component_measures/metric/${metric.key}/treemap`,
- query: { id: component.key }
- }}>
- <IconTreemap />
- {translate('component_measures.tab.treemap')}
- </Link>
- </li>}
- </ul>
-
- {child}
- </div>
- );
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 moment from 'moment';
-import ComponentsList from './ComponentsList';
-import ListHeader from './ListHeader';
-import Spinner from '../../components/Spinner';
-import SourceViewer from '../../../../components/SourceViewer/SourceViewer';
-import ListFooter from '../../../../components/controls/ListFooter';
-
-export default class TreeView extends React.PureComponent {
- componentDidMount() {
- this.handleChangeBaseComponent(this.props.component);
- }
-
- componentDidUpdate(nextProps) {
- if (nextProps.metric !== this.props.metric) {
- this.handleChangeBaseComponent(this.props.component);
- }
-
- if (this.props.selected) {
- this.scrollToViewer();
- } else if (this.scrollTop) {
- this.scrollToStoredPosition();
- }
- }
-
- scrollToViewer() {
- const { container } = this.refs;
- const top = container.getBoundingClientRect().top + window.scrollY - 95 - 10;
-
- // scroll only to top
- if (window.scrollY > top) {
- window.scrollTo(0, top);
- }
- }
-
- scrollToStoredPosition() {
- window.scrollTo(0, this.scrollTop);
- this.scrollTop = null;
- }
-
- storeScrollPosition() {
- this.scrollTop = window.scrollY;
- }
-
- handleChangeBaseComponent(baseComponent) {
- const { metric, onStart } = this.props;
- const periodIndex = this.props.location.query.period || 1;
- onStart(baseComponent, metric, Number(periodIndex));
- }
-
- handleFetchMore() {
- this.props.onFetchMore();
- }
-
- changeSelected(selected) {
- this.props.onSelect(selected);
- }
-
- canDrilldown(component) {
- return !['FIL', 'UTS'].includes(component.qualifier);
- }
-
- handleClick(selected) {
- if (this.canDrilldown(selected)) {
- this.props.onDrilldown(selected);
- } else {
- this.storeScrollPosition();
- this.props.onSelect(selected);
- }
- }
-
- handleBreadcrumbClick(component) {
- this.props.onUseBreadcrumbs(component, this.props.metric);
- }
-
- render() {
- const {
- components,
- metrics,
- breadcrumbs,
- metric,
- leakPeriod,
- selected,
- total,
- fetching
- } = this.props;
- const { onSelectNext, onSelectPrevious } = this.props;
-
- const selectedIndex = components.indexOf(selected);
- const sourceViewerPeriod = metric.key.indexOf('new_') === 0 && !!leakPeriod ? leakPeriod : null;
- const sourceViewerPeriodDate =
- sourceViewerPeriod != null ? moment(sourceViewerPeriod.date).toDate() : null;
-
- const filterLine =
- sourceViewerPeriodDate != null
- ? line => {
- if (line.scmDate) {
- const scmDate = moment(line.scmDate).toDate();
- return scmDate >= sourceViewerPeriodDate;
- } else {
- return false;
- }
- }
- : undefined;
-
- return (
- <div ref="container" className="measure-details-plain-list">
- <ListHeader
- metric={metric}
- breadcrumbs={breadcrumbs}
- componentsCount={components.length}
- selectedIndex={selectedIndex}
- onSelectPrevious={onSelectPrevious}
- onSelectNext={onSelectNext}
- onBrowse={this.handleBreadcrumbClick.bind(this)}
- />
-
- {!selected &&
- <div>
- {!fetching || components.length !== 0
- ? <div>
- <ComponentsList
- components={components}
- metrics={metrics}
- selected={selected}
- metric={metric}
- onClick={this.handleClick.bind(this)}
- />
- <ListFooter
- count={components.length}
- total={total}
- loadMore={this.handleFetchMore.bind(this)}
- />
- </div>
- : <Spinner />}
- </div>}
-
- {!!selected &&
- <div className="measure-details-viewer">
- <SourceViewer component={selected.key} filterLine={filterLine} />
- </div>}
- </div>
- );
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 { connect } from 'react-redux';
-import TreeView from './TreeView';
-import {
- start,
- drilldown,
- useBreadcrumbs,
- fetchMore,
- selectComponent,
- selectNext,
- selectPrevious
-} from '../../store/treeViewActions';
-import {
- getMeasuresAppTreeComponents,
- getMeasuresAppTreeBreadcrumbs,
- getMeasuresAppTreeSelected,
- getMeasuresAppTreeTotal,
- getMeasuresAppTreePageIndex,
- getMeasuresAppAllMetrics,
- getMeasuresAppDetailsMetric,
- isMeasuresAppFetching,
- getMeasuresAppComponent
-} from '../../../../store/rootReducer';
-
-const mapStateToProps = state => {
- return {
- components: getMeasuresAppTreeComponents(state),
- breadcrumbs: getMeasuresAppTreeBreadcrumbs(state),
- selected: getMeasuresAppTreeSelected(state),
- total: getMeasuresAppTreeTotal(state),
- pageIndex: getMeasuresAppTreePageIndex(state),
- component: getMeasuresAppComponent(state),
- metrics: getMeasuresAppAllMetrics(state),
- metric: getMeasuresAppDetailsMetric(state),
- fetching: isMeasuresAppFetching(state)
- };
-};
-
-const mapDispatchToProps = dispatch => {
- return {
- onStart: (rootComponent, metric, periodIndex) =>
- dispatch(start(rootComponent, metric, periodIndex)),
- onDrilldown: component => dispatch(drilldown(component)),
- onUseBreadcrumbs: component => dispatch(useBreadcrumbs(component)),
- onFetchMore: () => dispatch(fetchMore()),
- onSelect: component => dispatch(selectComponent(component)),
- onSelectNext: component => dispatch(selectNext(component)),
- onSelectPrevious: component => dispatch(selectPrevious(component))
- };
-};
-
-export default connect(mapStateToProps, mapDispatchToProps)(TreeView);
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 { DISPLAY_HOME } from '../app/actions';
-import { REQUEST_MEASURE, RECEIVE_MEASURE } from './actions';
-
-const initialState = {
- metric: undefined,
- secondaryMeasure: undefined,
- measure: undefined,
- periods: undefined
-};
-
-export default function appReducer(state = initialState, action = {}) {
- switch (action.type) {
- case DISPLAY_HOME:
- return initialState;
- case REQUEST_MEASURE:
- return { ...state, metric: action.metric };
- case RECEIVE_MEASURE:
- return {
- ...state,
- measure: action.measure,
- secondaryMeasure: action.secondaryMeasure,
- periods: action.periods
- };
- default:
- return state;
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 { scaleLinear, scaleOrdinal } from 'd3-scale';
-import Spinner from './../../components/Spinner';
-import { getLeakValue } from '../../utils';
-import { Treemap } from '../../../../components/charts/treemap';
-import { getChildren } from '../../../../api/components';
-import { formatMeasure } from '../../../../helpers/measures';
-import {
- translate,
- translateWithParameters,
- getLocalizedMetricName
-} from '../../../../helpers/l10n';
-import { getComponentUrl } from '../../../../helpers/urls';
-import Workspace from '../../../../components/workspace/main';
-
-const HEIGHT = 500;
-
-export default class MeasureTreemap extends React.PureComponent {
- state = {
- fetching: true,
- components: [],
- breadcrumbs: []
- };
-
- componentDidMount() {
- const { component } = this.props;
-
- this.mounted = true;
- this.fetchComponents(component.key);
- }
-
- componentDidUpdate(nextProps) {
- if (nextProps.metric !== this.props.metric) {
- this.fetchComponents(this.props.component.key);
- }
- }
-
- componentWillUnmount() {
- this.mounted = false;
- }
-
- fetchComponents(componentKey) {
- const { metric } = this.props;
- const metrics = ['ncloc', metric.key];
- const options = {
- s: 'metric',
- metricSort: 'ncloc',
- asc: false
- };
-
- return getChildren(componentKey, metrics, options).then(r => {
- const components = r.components.map(component => {
- const measures = {};
- const key = component.refKey || component.key;
-
- component.measures.forEach(measure => {
- const shouldUseLeak = measure.metric.indexOf('new_') === 0;
- measures[measure.metric] = shouldUseLeak ? getLeakValue(measure) : measure.value;
- });
- return { ...component, measures, key };
- });
-
- this.setState({
- components,
- fetching: false
- });
- });
- }
-
- getTooltip(component) {
- const { metric } = this.props;
-
- let inner = [
- component.name,
- `${translate('metric.ncloc.name')}: ${formatMeasure(component.measures['ncloc'], 'INT')}`
- ];
-
- const colorMeasure = component.measures[metric.key];
- const formatted = colorMeasure != null ? formatMeasure(colorMeasure, metric.type) : '—';
- inner.push(`${getLocalizedMetricName(metric)}: ${formatted}`);
- inner = inner.join('<br>');
- return `<div class="text-left">${inner}</div>`;
- }
- getPercentColorScale(metric) {
- const color = scaleLinear().domain([0, 25, 50, 75, 100]);
- color.range(
- metric.direction === 1
- ? ['#d4333f', '#ed7d20', '#eabe06', '#b0d513', '#00aa00']
- : ['#00aa00', '#b0d513', '#eabe06', '#ed7d20', '#d4333f']
- );
- return color;
- }
- getRatingColorScale() {
- return scaleLinear()
- .domain([1, 2, 3, 4, 5])
- .range(['#00aa00', '#b0d513', '#eabe06', '#ed7d20', '#d4333f']);
- }
- getLevelColorScale() {
- return scaleOrdinal()
- .domain(['ERROR', 'WARN', 'OK', 'NONE'])
- .range(['#d4333f', '#ed7d20', '#00aa00', '#b4b4b4']);
- }
- getScale() {
- const { metric } = this.props;
- if (metric.type === 'LEVEL') {
- return this.getLevelColorScale();
- }
- if (metric.type === 'RATING') {
- return this.getRatingColorScale();
- }
- return this.getPercentColorScale(metric);
- }
- handleRectangleClick(node) {
- const isFile = node.qualifier === 'FIL' || node.qualifier === 'UTS';
- if (isFile) {
- Workspace.openComponent({ key: node.key });
- return;
- }
- this.fetchComponents(node.key).then(() => {
- let nextBreadcrumbs = [...this.state.breadcrumbs];
- const index = this.state.breadcrumbs.findIndex(b => b.key === node.key);
- if (index !== -1) {
- nextBreadcrumbs = nextBreadcrumbs.slice(0, index);
- }
- nextBreadcrumbs = [
- ...nextBreadcrumbs,
- {
- key: node.key,
- name: node.name,
- qualifier: node.qualifier
- }
- ];
- this.setState({ breadcrumbs: nextBreadcrumbs });
- });
- }
- handleReset() {
- const { component } = this.props;
- this.fetchComponents(component.key).then(() => {
- this.setState({ breadcrumbs: [] });
- });
- }
- renderTreemap() {
- const { metric } = this.props;
- const colorScale = this.getScale();
- const items = this.state.components
- .filter(component => component.measures['ncloc'])
- .map(component => {
- const colorMeasure = component.measures[metric.key];
- return {
- id: component.id,
- key: component.key,
- name: component.name,
- qualifier: component.qualifier,
- size: component.measures['ncloc'],
- color: colorMeasure != null ? colorScale(colorMeasure) : '#777',
- tooltip: this.getTooltip(component),
- label: component.name,
- link: getComponentUrl(component.key)
- };
- });
- return (
- <Treemap
- items={items}
- breadcrumbs={this.state.breadcrumbs}
- height={HEIGHT}
- canBeClicked={() => true}
- onRectangleClick={this.handleRectangleClick.bind(this)}
- onReset={this.handleReset.bind(this)}
- />
- );
- }
- render() {
- const { metric } = this.props;
- const { fetching } = this.state;
- if (fetching) {
- return (
- <div className="measure-details-treemap">
- <div className="note text-center" style={{ lineHeight: `${HEIGHT}px` }}>
- <Spinner />
- </div>
- </div>
- );
- }
- return (
- <div className="measure-details-treemap">
- <ul className="list-inline note measure-details-treemap-legend">
- <li>
- {translateWithParameters(
- 'component_measures.legend.color_x',
- getLocalizedMetricName(metric)
- )}
- </li>
- <li>
- {translateWithParameters(
- 'component_measures.legend.size_x',
- translate('metric.ncloc.name')
- )}
- </li>
- </ul>
- {this.renderTreemap()}
- </div>
- );
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 { connect } from 'react-redux';
-import MeasureTreemap from './MeasureTreemap';
-import {
- getMeasuresAppDetailsMetric,
- getMeasuresAppComponent
-} from '../../../../store/rootReducer';
-
-const mapStateToProps = state => {
- return {
- component: getMeasuresAppComponent(state),
- metric: getMeasuresAppDetailsMetric(state)
- };
-};
-
-const mapDispatchToProps = () => {
- return {};
-};
-
-export default connect(mapStateToProps, mapDispatchToProps)(MeasureTreemap);
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 AllMeasuresDomain from './AllMeasuresDomain';
-import { getLeakPeriodLabel } from '../../../helpers/periods';
-
-export default function AllMeasures(props) {
- const { component, domains, periods } = props;
- const leakPeriodLabel = getLeakPeriodLabel(periods);
-
- return (
- <ul className="measures-domains">
- {domains.map(domain =>
- <AllMeasuresDomain
- key={domain.name}
- domain={domain}
- component={component}
- leakPeriodLabel={leakPeriodLabel}
- />
- )}
- </ul>
- );
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 { connect } from 'react-redux';
-import AllMeasures from './AllMeasures';
-import {
- getMeasuresAppHomeDomains,
- getMeasuresAppHomePeriods,
- getMeasuresAppComponent
-} from '../../../store/rootReducer';
-
-const mapStateToProps = state => {
- return {
- component: getMeasuresAppComponent(state),
- domains: getMeasuresAppHomeDomains(state),
- periods: getMeasuresAppHomePeriods(state)
- };
-};
-
-export default connect(mapStateToProps)(AllMeasures);
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 PropTypes from 'prop-types';
-import HomeMeasuresList from './HomeMeasuresList';
-import { getLocalizedMetricDomain } from '../../../helpers/l10n';
-
-export default function AllMeasuresDomain(props) {
- const { domain, component, displayHeader } = props;
-
- return (
- <li>
- {displayHeader &&
- <header className="page-header">
- <h3 className="page-title">
- {getLocalizedMetricDomain(domain.name)}
- </h3>
- </header>}
-
- <HomeMeasuresList domain={domain} component={component} />
- </li>
- );
-}
-
-AllMeasuresDomain.defaultProps = {
- displayHeader: true
-};
-
-AllMeasuresDomain.propTypes = {
- displayHeader: PropTypes.bool
-};
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 HomeMeasuresList from './HomeMeasuresList';
-import MeasureBubbleChartContainer from '../components/bubbleChart/MeasureBubbleChartContainer';
-import { hasBubbleChart } from '../utils';
-
-export default function DomainMeasures(props) {
- const { component, domains } = props;
- const { domainName } = props.params;
- const domain = domains.find(d => d.name === domainName);
-
- return (
- <section id="component-measures-domain">
- <HomeMeasuresList domain={domain} component={component} />
-
- {hasBubbleChart(domainName) && <MeasureBubbleChartContainer domainName={domainName} />}
- </section>
- );
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 { connect } from 'react-redux';
-import DomainMeasures from './DomainMeasures';
-import {
- getMeasuresAppHomeDomains,
- getMeasuresAppHomePeriods,
- getMeasuresAppComponent
-} from '../../../store/rootReducer';
-
-const mapStateToProps = state => {
- return {
- component: getMeasuresAppComponent(state),
- domains: getMeasuresAppHomeDomains(state),
- periods: getMeasuresAppHomePeriods(state)
- };
-};
-
-export default connect(mapStateToProps)(DomainMeasures);
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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, IndexLink } from 'react-router';
-import LeakPeriodLegend from '../components/LeakPeriodLegend';
-import { getLeakPeriod } from '../../../helpers/periods';
-import { translate, getLocalizedMetricDomain } from '../../../helpers/l10n';
-
-export default class Home extends React.PureComponent {
- componentDidMount() {
- document.querySelector('html').classList.add('dashboard-page');
- this.props.onDisplay();
- this.props.fetchMeasures();
- }
-
- componentWillUnmount() {
- document.querySelector('html').classList.remove('dashboard-page');
- }
-
- render() {
- const { component, domains, periods } = this.props;
-
- if (domains == null) {
- return null;
- }
-
- const leakPeriod = getLeakPeriod(periods);
-
- return (
- <section id="component-measures-home" className="page page-container page-limited">
- <header id="component-measures-home-header" className="home-header">
- <nav className="nav-pills pull-left">
- <ul>
- <li>
- <IndexLink
- to={{ pathname: '/component_measures', query: { id: component.key } }}
- activeClassName="active">
- {translate('all')}
- </IndexLink>
- </li>
- {domains.map(domain =>
- <li key={domain.name}>
- <Link
- to={{
- pathname: `/component_measures/domain/${domain.name}`,
- query: { id: component.key }
- }}
- activeClassName="active">
- {getLocalizedMetricDomain(domain.name)}
- </Link>
- </li>
- )}
- </ul>
- </nav>
-
- {leakPeriod != null && <LeakPeriodLegend component={component} period={leakPeriod} />}
- </header>
-
- <main id="component-measures-home-main">
- {this.props.children}
- </main>
- </section>
- );
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 { connect } from 'react-redux';
-import Home from './Home';
-import { fetchMeasures } from './actions';
-import { displayHome } from '../app/actions';
-import {
- getMeasuresAppHomeDomains,
- getMeasuresAppHomePeriods,
- getMeasuresAppComponent
-} from '../../../store/rootReducer';
-
-const mapStateToProps = state => {
- return {
- component: getMeasuresAppComponent(state),
- domains: getMeasuresAppHomeDomains(state),
- periods: getMeasuresAppHomePeriods(state)
- };
-};
-
-const mapDispatchToProps = dispatch => {
- return {
- onDisplay: () => dispatch(displayHome()),
- fetchMeasures: () => dispatch(fetchMeasures())
- };
-};
-
-export default connect(mapStateToProps, mapDispatchToProps)(Home);
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 { partition, sortBy } from 'lodash';
-import MeasuresList from './MeasuresList';
-import { domains } from '../config/domains';
-import { getLocalizedMetricName } from '../../../helpers/l10n';
-
-function sortMeasures(measures, order) {
- const [known, unknown] = partition(measures, measure => order.includes(measure.metric.key));
- return [
- ...sortBy(known, measure => order.indexOf(measure.metric.key)),
- ...sortBy(unknown, measure => getLocalizedMetricName(measure.metric))
- ];
-}
-
-function filterIssuesMeasures(measures) {
- const BANNED_MEASURES = [
- 'blocker_violations',
- 'new_blocker_violations',
- 'critical_violations',
- 'new_critical_violations',
- 'major_violations',
- 'new_major_violations',
- 'minor_violations',
- 'new_minor_violations',
- 'info_violations',
- 'new_info_violations'
- ];
- return measures.filter(measure => !BANNED_MEASURES.includes(measure.metric.key));
-}
-
-const HomeMeasuresList = ({ domain, component }) => {
- const { measures, name } = domain;
- const config = domains[name] || {};
-
- const filteredMeasures = filterIssuesMeasures(measures);
-
- const configMain = config.main || [];
- const [mainMeasures, otherMeasures] = partition(filteredMeasures, measure =>
- configMain.includes(measure.metric.key)
- );
-
- const configOrder = config.order || [];
- const sortedMainMeasures = sortMeasures(mainMeasures, configOrder);
- const sortedOtherMeasures = sortMeasures(otherMeasures, configOrder);
-
- return (
- <div className="home-measures-list clearfix">
- {sortedMainMeasures.length > 0 &&
- <MeasuresList
- className="main-domain-measures"
- measures={sortedMainMeasures}
- component={component}
- spaces={[]}
- />}
-
- {sortedOtherMeasures.length > 0 &&
- <MeasuresList measures={sortedOtherMeasures} component={component} spaces={[]} />}
- </div>
- );
-};
-
-export default HomeMeasuresList;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 Measure from '../components/Measure';
-import { isDiffMetric } from '../../../helpers/measures';
-
-const MeasureListValue = ({ measure }) => {
- const { metric } = measure;
-
- if (isDiffMetric(metric.key)) {
- return (
- <div
- id={`measure-${measure.metric.key}-leak`}
- className="domain-measures-value domain-measures-leak">
- <Measure measure={measure} />
- </div>
- );
- }
-
- return (
- <div id={`measure-${measure.metric.key}-value`} className="domain-measures-value">
- <Measure measure={measure} />
- </div>
- );
-};
-
-export default MeasureListValue;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 MeasureListValue from './MeasureListValue';
-import { getLocalizedMetricName } from '../../../helpers/l10n';
-import IssueTypeIcon from '../../../components/ui/IssueTypeIcon';
-
-const MeasuresList = ({ measures, component, className = 'domain-measures' }) => {
- return (
- <ul className={className}>
- {measures.map(measure =>
- <li key={measure.metric.key} id={`measure-${measure.metric.key}`}>
- <Link
- to={{
- pathname: `/component_measures/metric/${measure.metric.key}`,
- query: { id: component.key }
- }}>
- <div className="domain-measures-name">
- <IssueTypeIcon query={measure.metric.key} className="little-spacer-right" />
- <span id={`measure-${measure.metric.key}-name`}>
- {getLocalizedMetricName(measure.metric)}
- </span>
- </div>
-
- <MeasureListValue measure={measure} />
- </Link>
- </li>
- )}
- </ul>
- );
-};
-
-export default MeasuresList;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 { startFetching, stopFetching } from '../store/statusActions';
-import { getMeasuresAndMeta } from '../../../api/measures';
-import { getLeakValue } from '../utils';
-import { getMeasuresAppComponent, getMeasuresAppAllMetrics } from '../../../store/rootReducer';
-
-export const RECEIVE_MEASURES = 'measuresApp/home/RECEIVE_MEASURES';
-
-export function receiveMeasures(measures, periods) {
- return { type: RECEIVE_MEASURES, measures, periods };
-}
-
-function banQualityGate(component, measures) {
- let newMeasures = [...measures];
-
- if (!['VW', 'SVW', 'APP'].includes(component.qualifier)) {
- newMeasures = newMeasures.filter(measure => measure.metric !== 'alert_status');
- }
-
- if (component.qualifier === 'APP') {
- newMeasures = newMeasures.filter(
- measure =>
- measure.metric !== 'releasability_rating' && measure.metric !== 'releasability_effort'
- );
- }
-
- return newMeasures;
-}
-
-export function fetchMeasures() {
- return (dispatch, getState) => {
- dispatch(startFetching());
-
- const state = getState();
- const component = getMeasuresAppComponent(state);
- const metrics = getMeasuresAppAllMetrics(state);
-
- const metricKeys = metrics
- .filter(metric => !metric.hidden)
- .filter(metric => metric.type !== 'DATA' && metric.type !== 'DISTRIB')
- .map(metric => metric.key);
-
- getMeasuresAndMeta(component.key, metricKeys, { additionalFields: 'periods' }).then(r => {
- const measures = banQualityGate(component, r.component.measures)
- .map(measure => {
- const metric = metrics.find(metric => metric.key === measure.metric);
- const leak = getLeakValue(measure);
- return { ...measure, metric, leak };
- })
- .filter(measure => {
- const hasValue = measure.value != null;
- const hasLeakValue = measure.leak != null;
- return hasValue || hasLeakValue;
- });
-
- const newBugs = measures.find(measure => measure.metric.key === 'new_bugs');
-
- const applicationPeriods = newBugs ? [{ index: 1 }] : [];
- const periods = component.qualifier === 'APP' ? applicationPeriods : r.periods;
-
- dispatch(receiveMeasures(measures, periods));
- dispatch(stopFetching());
- });
- };
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 { groupBy, partition, sortBy, toPairs } from 'lodash';
-import { RECEIVE_MEASURES } from './actions';
-import { getLocalizedMetricName } from '../../../helpers/l10n';
-
-const initialState = {
- measures: undefined,
- domains: undefined,
- periods: undefined
-};
-
-function groupByDomains(measures) {
- const KNOWN_DOMAINS = [
- 'Releasability',
- 'Reliability',
- 'Security',
- 'Maintainability',
- 'Coverage',
- 'Duplications',
- 'Size',
- 'Complexity'
- ];
-
- const domains = sortBy(
- toPairs(groupBy(measures, measure => measure.metric.domain)).map(r => {
- const [name, measures] = r;
- const sortedMeasures = sortBy(measures, measure => getLocalizedMetricName(measure.metric));
-
- return { name, measures: sortedMeasures };
- }),
- 'name'
- );
- const [knownDomains, unknownDomains] = partition(domains, domain =>
- KNOWN_DOMAINS.includes(domain.name)
- );
- return [
- ...sortBy(knownDomains, domain => KNOWN_DOMAINS.indexOf(domain.name)),
- ...sortBy(unknownDomains, domain => domain.name)
- ];
-}
-
-export default function(state = initialState, action = {}) {
- switch (action.type) {
- case RECEIVE_MEASURES:
- return {
- ...state,
- measures: action.measures,
- domains: groupByDomains(action.measures),
- periods: action.periods
- };
- default:
- return state;
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 { isDiffMetric } from '../../helpers/measures';
-
-export function checkHistoryExistence(nextState, replace) {
- const { metricKey } = nextState.params;
-
- if (isDiffMetric(metricKey)) {
- replace({
- pathname: metricKey,
- query: nextState.location.query
- });
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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.
- */
-const routes = [
- {
- getComponent(_, callback) {
- import('./app/AppContainer').then(i => callback(null, i.default));
- },
- childRoutes: [
- {
- getComponent(_, callback) {
- import('./home/HomeContainer').then(i => callback(null, i.default));
- },
- childRoutes: [
- {
- getIndexRoute(_, callback) {
- import('./home/AllMeasuresContainer').then(i =>
- callback(null, { component: i.default })
- );
- }
- },
- {
- path: 'domain/:domainName',
- getComponent(_, callback) {
- import('./home/DomainMeasuresContainer').then(i => callback(null, i.default));
- }
- }
- ]
- },
- {
- path: 'metric/:metricKey',
- getComponent(_, callback) {
- import('./details/MeasureDetailsContainer').then(i => callback(null, i.default));
- },
- childRoutes: [
- {
- indexRoute: {
- onEnter(nextState, replace) {
- const { params, location } = nextState;
- replace({
- pathname: `/component_measures/metric/${params.metricKey}/list`,
- query: location.query
- });
- }
- }
- },
- {
- path: 'list',
- getComponent(_, callback) {
- import('./details/drilldown/ListViewContainer').then(i => callback(null, i.default));
- }
- },
- {
- path: 'tree',
- getComponent(_, callback) {
- import('./details/drilldown/TreeViewContainer').then(i => callback(null, i.default));
- }
- },
- {
- path: 'history',
- onEnter(nextState, replace) {
- replace({
- pathname: '/project/activity',
- query: {
- id: nextState.location.query.id,
- graph: 'custom',
- custom_metrics: nextState.params.metricKey
- }
- });
- }
- },
- {
- path: 'treemap',
- getComponent(_, callback) {
- import('./details/treemap/MeasureTreemapContainer').then(i =>
- callback(null, i.default)
- );
- }
- }
- ]
- }
- ]
- }
-];
-
-export default routes;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 { getComponentTree } from '../../../api/components';
-import { enhanceWithMeasure } from '../utils';
-import { startFetching, stopFetching } from './statusActions';
-import complementary from '../config/complementary';
-import { getMeasuresAppList } from '../../../store/rootReducer';
-
-export const UPDATE_STORE = 'measuresApp/drilldown/list/UPDATE_STORE';
-
-function updateStore(state) {
- return { type: UPDATE_STORE, state };
-}
-
-function getComplementary(metric) {
- const comp = complementary[metric] || [];
- return [metric, ...comp];
-}
-
-function makeRequest(baseComponent, metric, options, periodIndex = 1) {
- const asc = metric.direction === 1;
- const ps = 100;
- const finalOptions = { asc, ps, metricSortFilter: 'withMeasuresOnly' };
-
- if (metric.key.indexOf('new_') === 0) {
- Object.assign(options, {
- s: 'metricPeriod,name',
- metricSort: metric.key,
- metricPeriodSort: periodIndex
- });
- } else {
- Object.assign(options, {
- s: 'metric,name',
- metricSort: metric.key
- });
- }
-
- Object.assign(finalOptions, options);
- return getComponentTree('leaves', baseComponent.key, getComplementary(metric.key), finalOptions);
-}
-
-function fetchLeaves(baseComponent, metric, pageIndex = 1, periodIndex = 1) {
- const options = { p: pageIndex };
-
- return makeRequest(baseComponent, metric, options, periodIndex).then(r => {
- const nextComponents = enhanceWithMeasure(r.components, metric.key, periodIndex);
-
- return {
- components: nextComponents,
- pageIndex: r.paging.pageIndex,
- total: r.paging.total
- };
- });
-}
-
-/**
- * Fetch the first page of components for a given base component
- * @param baseComponent
- * @param metric
- * @param periodIndex
- */
-export function fetchList(baseComponent, metric, periodIndex = 1) {
- return (dispatch, getState) => {
- const list = getMeasuresAppList(getState());
- if (list.baseComponent === baseComponent && list.metric === metric) {
- return Promise.resolve();
- }
-
- dispatch(startFetching());
- return fetchLeaves(baseComponent, metric, 1, periodIndex).then(r => {
- dispatch(
- updateStore({
- ...r,
- baseComponent,
- metric
- })
- );
- dispatch(stopFetching());
- });
- };
-}
-
-export function fetchMore(periodIndex) {
- return (dispatch, getState) => {
- const { baseComponent, metric, pageIndex, components } = getMeasuresAppList(getState());
- dispatch(startFetching());
- return fetchLeaves(baseComponent, metric, pageIndex + 1, periodIndex).then(r => {
- const nextComponents = [...components, ...r.components];
- dispatch(updateStore({ ...r, components: nextComponents }));
- dispatch(stopFetching());
- });
- };
-}
-
-/**
- * Select specified component from the list
- * @param component A component to select
- */
-export function selectComponent(component) {
- return dispatch => {
- dispatch(updateStore({ selected: component }));
- };
-}
-
-/**
- * Select next element from the list of components
- */
-export function selectNext() {
- return (dispatch, getState) => {
- const { components, selected } = getMeasuresAppList(getState());
- const selectedIndex = components.indexOf(selected);
- if (selectedIndex < components.length - 1) {
- const nextSelected = components[selectedIndex + 1];
- dispatch(selectComponent(nextSelected));
- }
- };
-}
-
-/**
- * Select previous element from the list of components
- */
-export function selectPrevious() {
- return (dispatch, getState) => {
- const { components, selected } = getMeasuresAppList(getState());
- const selectedIndex = components.indexOf(selected);
- if (selectedIndex > 0) {
- const nextSelected = components[selectedIndex - 1];
- dispatch(selectComponent(nextSelected));
- }
- };
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 { DISPLAY_HOME } from './../app/actions';
-import { UPDATE_STORE } from './listViewActions';
-
-const initialState = {
- components: [],
- total: 0
-};
-
-export default function drilldownReducer(state = initialState, action = {}) {
- switch (action.type) {
- case DISPLAY_HOME:
- return initialState;
- case UPDATE_STORE:
- return { ...state, ...action.state };
- default:
- return state;
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 { combineReducers } from 'redux';
-import appReducer from './../app/reducer';
-import statusReducer from './statusReducer';
-import homeReducer from '../home/reducer';
-import detailsReducer from '../details/reducer';
-import listViewReducer from './listViewReducer';
-import treeViewReducer from './treeViewReducer';
-
-export default combineReducers({
- app: appReducer,
- home: homeReducer,
- details: detailsReducer,
- list: listViewReducer,
- tree: treeViewReducer,
- status: statusReducer
-});
-
-export const getComponent = state => state.app.component;
-
-export const getAllMetrics = state => state.app.metrics;
-
-export const getDetailsMetric = state => state.details.metric;
-
-export const getDetailsMeasure = state => state.details.measure;
-
-export const getDetailsSecondaryMeasure = state => state.details.secondaryMeasure;
-
-export const getDetailsPeriods = state => state.details.periods;
-
-export const isFetching = state => state.status.fetching;
-
-export const getList = state => state.list;
-
-export const getListComponents = state => state.list.components;
-
-export const getListSelected = state => state.list.selected;
-
-export const getListTotal = state => state.list.total;
-
-export const getListPageIndex = state => state.list.pageIndex;
-
-export const getTree = state => state.tree;
-
-export const getTreeComponents = state => state.tree.components;
-
-export const getTreeBreadcrumbs = state => state.tree.breadcrumbs;
-
-export const getTreeSelected = state => state.tree.selected;
-
-export const getTreeTotal = state => state.tree.total;
-
-export const getTreePageIndex = state => state.tree.pageIndex;
-
-export const getHomeDomains = state => state.home.domains;
-
-export const getHomePeriods = state => state.home.periods;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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.
- */
-export const START_FETCHING = 'measuresApp/status/START_FETCHING';
-export const STOP_FETCHING = 'measuresApp/status/STOP_FETCHING';
-
-export function startFetching() {
- return { type: START_FETCHING };
-}
-
-export function stopFetching() {
- return { type: STOP_FETCHING };
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 { START_FETCHING, STOP_FETCHING } from './statusActions';
-
-const initialState = {
- fetching: false
-};
-
-export default function drilldownReducer(state = initialState, action = {}) {
- switch (action.type) {
- case START_FETCHING:
- return { ...state, fetching: true };
- case STOP_FETCHING:
- return { ...state, fetching: false };
- default:
- return state;
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 { initial } from 'lodash';
-import { getComponentTree } from '../../../api/components';
-import { enhanceWithMeasure } from '../utils';
-import { startFetching, stopFetching } from './statusActions';
-import complementary from '../config/complementary';
-import { getMeasuresAppTree } from '../../../store/rootReducer';
-
-/*
- * Actions
- */
-
-export const UPDATE_STORE = 'measuresApp/drilldown/tree/UPDATE_STORE';
-export const INIT = 'measuresApp/drilldown/tree/INIT';
-
-/*
- * Action Creators
- */
-
-/**
- * Internal
- * Update store
- * @param state
- * @returns {{type: string, state: *}}
- */
-function updateStore(state) {
- return { type: UPDATE_STORE, state };
-}
-
-/**
- * Init tree view drilldown for the given root component and given metric
- * @param rootComponent
- * @param metric
- * @param periodIndex
- * @returns {{type: string, rootComponent: *, metric: *}}
- */
-function init(rootComponent, metric, periodIndex = 1) {
- return { type: INIT, rootComponent, metric, periodIndex };
-}
-
-/*
- * Workflow
- */
-
-function getComplementary(metric) {
- const comp = complementary[metric] || [];
- return [metric, ...comp];
-}
-
-function makeRequest(rootComponent, baseComponent, metric, options, periodIndex = 1) {
- const asc = metric.direction === 1;
- const ps = 100;
- const finalOptions = { asc, ps, metricSortFilter: 'withMeasuresOnly' };
-
- if (metric.key.indexOf('new_') === 0) {
- Object.assign(options, {
- s: 'metricPeriod,name',
- metricSort: metric.key,
- metricPeriodSort: periodIndex
- });
- } else {
- Object.assign(options, {
- s: 'metric,name',
- metricSort: metric.key
- });
- }
-
- if (rootComponent.qualifier === 'DEV' && baseComponent.qualifier !== 'DEV') {
- Object.assign(options, { developerId: rootComponent.id });
- }
-
- Object.assign(finalOptions, options);
-
- const finalKey = baseComponent.refKey || baseComponent.key;
-
- return getComponentTree('children', finalKey, getComplementary(metric.key), finalOptions);
-}
-
-function fetchComponents(rootComponent, baseComponent, metric, pageIndex = 1, periodIndex = 1) {
- const options = { p: pageIndex };
-
- return makeRequest(rootComponent, baseComponent, metric, options, periodIndex).then(r => {
- const nextComponents = enhanceWithMeasure(r.components, metric.key, periodIndex);
-
- return {
- baseComponent,
- components: nextComponents,
- pageIndex: r.paging.pageIndex,
- total: r.paging.total
- };
- });
-}
-
-/**
- * Fetch the first page of components for a given base component
- * @param baseComponent
- */
-function fetchList(baseComponent) {
- return (dispatch, getState) => {
- const { metric, periodIndex, rootComponent } = getMeasuresAppTree(getState());
-
- dispatch(startFetching());
- return fetchComponents(rootComponent, baseComponent, metric, 1, periodIndex).then(r => {
- dispatch(
- updateStore({
- ...r,
- baseComponent,
- breadcrumbs: [baseComponent]
- })
- );
- dispatch(stopFetching());
- });
- };
-}
-
-/**
- * Init tree view with root component and metric.
- * Fetch the first page of components if needed.
- * @param rootComponent
- * @param metric
- * @param periodIndex
- * @returns {function()}
- */
-export function start(rootComponent, metric, periodIndex = 1) {
- return (dispatch, getState) => {
- const tree = getMeasuresAppTree(getState());
- if (rootComponent === tree.rootComponent && metric === tree.metric) {
- return Promise.resolve();
- }
-
- dispatch(init(rootComponent, metric, periodIndex));
- dispatch(fetchList(rootComponent));
- };
-}
-
-/**
- * Drilldown to the component
- * @param component
- */
-export function drilldown(component) {
- return (dispatch, getState) => {
- const { metric, rootComponent, breadcrumbs, periodIndex } = getMeasuresAppTree(getState());
- dispatch(startFetching());
- return fetchComponents(rootComponent, component, metric, 1, periodIndex).then(r => {
- dispatch(
- updateStore({
- ...r,
- breadcrumbs: [...breadcrumbs, component],
- selected: undefined
- })
- );
- dispatch(stopFetching());
- });
- };
-}
-
-/**
- * Go up using breadcrumbs
- * @param component
- */
-export function useBreadcrumbs(component) {
- return (dispatch, getState) => {
- const { metric, rootComponent, breadcrumbs, periodIndex } = getMeasuresAppTree(getState());
- const index = breadcrumbs.indexOf(component);
- dispatch(startFetching());
- return fetchComponents(rootComponent, component, metric, 1, periodIndex).then(r => {
- dispatch(
- updateStore({
- ...r,
- breadcrumbs: breadcrumbs.slice(0, index + 1),
- selected: undefined
- })
- );
- dispatch(stopFetching());
- });
- };
-}
-
-export function fetchMore() {
- return (dispatch, getState) => {
- const {
- rootComponent,
- baseComponent,
- metric,
- pageIndex,
- components,
- periodIndex
- } = getMeasuresAppTree(getState());
- dispatch(startFetching());
- return fetchComponents(
- rootComponent,
- baseComponent,
- metric,
- pageIndex + 1,
- periodIndex
- ).then(r => {
- const nextComponents = [...components, ...r.components];
- dispatch(updateStore({ ...r, components: nextComponents }));
- dispatch(stopFetching());
- });
- };
-}
-
-/**
- * Select given component from the list
- * @param component
- */
-export function selectComponent(component) {
- return (dispatch, getState) => {
- const { breadcrumbs } = getMeasuresAppTree(getState());
- const nextBreadcrumbs = [...breadcrumbs, component];
- dispatch(
- updateStore({
- selected: component,
- breadcrumbs: nextBreadcrumbs
- })
- );
- };
-}
-
-/**
- * Select next element from the list of components
- */
-export function selectNext() {
- return (dispatch, getState) => {
- const { components, selected, breadcrumbs } = getMeasuresAppTree(getState());
- const selectedIndex = components.indexOf(selected);
- if (selectedIndex < components.length - 1) {
- const nextSelected = components[selectedIndex + 1];
- const nextBreadcrumbs = [...initial(breadcrumbs), nextSelected];
- dispatch(
- updateStore({
- selected: nextSelected,
- breadcrumbs: nextBreadcrumbs
- })
- );
- }
- };
-}
-
-/**
- * Select previous element from the list of components
- */
-export function selectPrevious() {
- return (dispatch, getState) => {
- const { components, selected, breadcrumbs } = getMeasuresAppTree(getState());
- const selectedIndex = components.indexOf(selected);
- if (selectedIndex > 0) {
- const nextSelected = components[selectedIndex - 1];
- const nextBreadcrumbs = [...initial(breadcrumbs), nextSelected];
- dispatch(
- updateStore({
- selected: nextSelected,
- breadcrumbs: nextBreadcrumbs
- })
- );
- }
- };
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 { pick } from 'lodash';
-import { DISPLAY_HOME } from './../app/actions';
-import { UPDATE_STORE, INIT } from './treeViewActions';
-
-const initialState = {
- components: [],
- breadcrumbs: [],
- total: 0
-};
-
-export default function drilldownReducer(state = initialState, action = {}) {
- switch (action.type) {
- case DISPLAY_HOME:
- return initialState;
- case UPDATE_STORE:
- return { ...state, ...action.state };
- case INIT:
- return { ...state, ...pick(action, ['rootComponent', 'metric', 'periodIndex']) };
- default:
- return state;
- }
-}
+++ /dev/null
-.home-header {
- display: flex;
- justify-content: space-between;
- align-items: flex-start;
- margin-bottom: 20px;
-}
-
-.measures-domains {
-}
-
-.measures-domains > li {
- margin-bottom: 20px;
-}
-
-.measures-domains > li > div {
- border: 1px solid #e6e6e6;
- background-color: #fff;
-}
-
-.measures-domains-leak-header {
- line-height: 22px;
- padding: 0 10px;
- border: 1px solid #eae3c7;
- background-color: #fbf3d5;
- white-space: nowrap;
-}
-
-.main-domain-measures {
- display: flex;
- flex-wrap: wrap;
- justify-content: space-around;
- float: left;
- width: 480px;
- margin-right: 60px;
-}
-
-.main-domain-measures > li {
- padding: 12px 0;
-}
-
-.main-domain-measures > li > a {
- display: flex;
- flex-direction: column-reverse;
- width: 160px;
- border: none;
- text-align: center;
-}
-
-.main-domain-measures .domain-measures-value {
- height: 40px;
- box-sizing: border-box;
- color: #444;
- font-size: 30px;
- font-weight: 300;
-}
-
-.main-domain-measures .domain-measures-value .rating,
-.measure-details-value .rating {
- vertical-align: top;
- width: 40px;
- height: 40px;
- line-height: 40px;
- border-radius: 40px;
- font-size: 24px;
-}
-
-.main-domain-measures .domain-measures-name {
- margin-top: 8px;
-}
-
-.main-domain-measures .domain-measures-name > span:last-child {
- border-bottom: 1px solid #cae3f2;
-}
-
-.main-domain-measures .domain-measures-leak {
- margin: 0 20px;
- border: 1px solid #eae3c7;
- background-color: #fbf3d5;
-}
-
-.domain-measures {
- overflow: hidden;
- line-height: 1.4;
-}
-
-.domain-measures > li > a {
- display: flex;
- justify-content: space-between;
- border: none;
-}
-
-.domain-measures > li:nth-child(odd) > a {
- background-color: #f8f8f8;
-}
-
-.domain-measures > li > a:hover {
- background-color: #ecf6fe !important;
-}
-
-.domain-measures .domain-measures-name,
-.domain-measures .domain-measures-value {
- padding: 7px 10px;
- box-sizing: border-box;
-}
-
-.domain-measures .domain-measures-name {
- width: calc(100% - 160px);
- line-height: 24px;
-}
-
-.domain-measures .domain-measures-name > span {
- border-bottom: 1px solid #cae3f2;
-}
-
-.domain-measures .domain-measures-value {
- min-width: 80px;
- color: #444;
- text-align: right;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
-}
-
-.domain-measures .domain-measures-leak {
- background-color: #fbf3d5;
- transition: background-color 0.3s ease;
-}
-
-.domain-measures .domain-measures > li:nth-child(odd) .domain-measures-leak {
- background-color: #f5eed0;
-}
-
-.measure-details {
- margin-top: 10px;
-}
-
-.measure-details-header {
- position: relative;
- margin-top: 10px;
- margin-bottom: 30px;
-}
-
-.measure-details-metric,
-.measure-details-value {
-}
-
-.measure-details-metric {
- display: inline-block;
- margin-bottom: 10px;
-}
-
-.measure-details-metric > a:not(.button) {
- border: none;
- color: #444;
-}
-
-.measure-details-metric > a:not(.button):hover {
- color: #236a97;
-}
-
-.measure-details-value {
- font-size: 24px;
-}
-
-.measure-details-value-absolute {
- display: inline-block;
- vertical-align: middle;
- padding: 5px 0;
- font-weight: 300;
-}
-
-.measure-details-value-leak {
- display: inline-block;
- vertical-align: middle;
- padding: 4px 10px;
- border: 1px solid #eae3c7;
- background-color: #fbf3d5;
- font-weight: 300;
-}
-
-.measure-details-value-absolute + .measure-details-value-leak {
- margin-left: 20px;
-}
-
-.measure-details-secondary {
- display: inline-block;
- vertical-align: middle;
- width: 260px;
- margin-left: 20px;
-}
-
-.measure-details-drilldown {
- margin-top: 20px;
-}
-
-.measure-details-drilldown-mode {
- display: flex;
- margin-bottom: 10px;
- border-bottom: 1px solid #e6e6e6;
-}
-
-.measure-details-drilldown-mode > li {
- margin-bottom: -1px;
-}
-
-.measure-details-drilldown-mode > li + li {
- margin-left: 2px;
-}
-
-.measure-details-drilldown-mode > li > a {
- display: inline-block;
- padding: 5px 10px;
- border-bottom: 2px solid transparent;
- color: #444;
-}
-
-.measure-details-drilldown-mode > li > a:hover,
-.measure-details-drilldown-mode > li > a.active {
- border-bottom-color: #4b9fd5;
-}
-
-.measure-details-drilldown-mode > li > a.active .measure-tab-icon path {
- fill: #4b9fd5;
-}
-
-.measure-details-plain-list {
-}
-
-.measure-details-components {
- width: 300px;
- padding: 10px;
- box-sizing: border-box;
-}
-
-.measure-details-components > li > a {
- display: flex;
- padding-top: 5px;
- padding-bottom: 5px;
- border: none;
- color: #444;
-}
-
-.measure-details-components > li > a:hover,
-.measure-details-components > li > a.selected {
- background-color: #cae3f2 !important;
-}
-
-.measure-details-component-name,
-.measure-details-component-value {
- padding-right: 10px;
- box-sizing: border-box;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
-}
-
-.measure-details-component-name {
- width: calc(100% - 60px);
- padding-left: 10px;
-}
-
-.measure-details-component-value {
- width: 60px;
- text-align: right;
-}
-
-.measure-details-viewer {
- min-height: 100vh;
-}
-
-.measure-details-viewer-header {
- margin-bottom: 10px;
- line-height: 24px;
-}
-
-.measure-details-viewer-header .button-group {
- vertical-align: top;
-}
-
-.measure-details-header-container {
- display: inline-block;
- line-height: 16px;
- padding-right: 10px;
-}
-
-.measure-tab-icon {
- display: inline-block;
- width: 14px;
- height: 14px;
- margin-right: 6px;
-}
-
-.measure-tab-icon path {
- fill: #aeaeae;
- transition: fill 0.3s ease;
-}
-
-.measure-details-components-up-icon path {
- fill: #aeaeae;
-}
-
-.measure-details-history {
- padding: 10px 0;
-}
-
-.measure-details-bubble-chart {
- position: relative;
- margin: 40px 0 10px;
- padding: 30px 0 30px 60px;
- border: 1px solid #e6e6e6;
- background-color: #fff;
-}
-
-.measure-details-bubble-chart-axis {
- position: absolute;
- color: #777;
- font-size: 12px;
-}
-
-.measure-details-bubble-chart-axis.x {
- left: 50%;
- bottom: 10px;
- width: 500px;
- margin-left: -250px;
- text-align: center;
-}
-
-.measure-details-bubble-chart-axis.y {
- top: 50%;
- left: -20px;
- transform: rotate(-90deg);
-}
-
-.measure-details-bubble-chart-axis.size {
- left: 50%;
- top: 10px;
- width: 500px;
- margin-left: -250px;
- text-align: center;
-}
-
-.measure-details-treemap {
- margin: 20px 0;
-}
-
-.measure-details-treemap-legend {
- margin-bottom: 10px;
-}
-
-.component-measures-breadcrumbs {
- display: flex;
- flex-wrap: wrap;
-}
-
-.component-measures-breadcrumbs > li {
- padding: 5px 5px 3px;
-}
-
-.component-measures-breadcrumbs > li:first-child {
- padding-left: 0;
-}
-
-.component-measures-breadcrumbs > li::after {
- position: relative;
- top: -1px;
- padding-left: 10px;
- color: #777;
- font-size: 11px;
- content: ">";
-}
-
-.component-measures-breadcrumbs > li:last-child::after {
- display: none;
-}
-
-.home-measures-list {
- border: 1px solid #e6e6e6;
- background-color: #fff;
-}
-
-.nav-pills > ul {
- display: flex;
- flex-wrap: wrap;
-}
-
-.nav-pills > ul > li > a {
- display: inline-block;
- vertical-align: middle;
- padding: 3px 10px;
- border: 1px solid transparent;
- border-radius: 24px;
- color: #236a97;
- transition: none;
-}
-
-.nav-pills > ul > li > a:hover {
- border-color: #236a97;
-}
-
-.nav-pills > ul > li.active > a,
-.nav-pills > ul > li > a.active {
- background-color: #236a97;
- color: #fff;
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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 bubbles from './config/bubbles';
-import {
- formatMeasure,
- formatMeasureVariation,
- getRatingTooltip as nextGetRatingTooltip,
- isDiffMetric
-} from '../../helpers/measures';
-
-export function getLeakValue(measure, periodIndex = 1) {
- if (!measure) {
- return null;
- }
-
- const period = measure.periods
- ? measure.periods.find(period => period.index === periodIndex)
- : null;
-
- return period ? period.value : null;
-}
-
-export function getSingleMeasureValue(measures) {
- if (!measures || !measures.length) {
- return null;
- }
-
- return measures[0].value;
-}
-
-export function getSingleLeakValue(measures, periodIndex = 1) {
- if (!measures || !measures.length) {
- return null;
- }
-
- const measure = measures[0];
-
- const period = measure.periods
- ? measure.periods.find(period => period.index === periodIndex)
- : null;
-
- return period ? period.value : null;
-}
-
-export function formatLeak(value, metric, options) {
- if (isDiffMetric(metric.key)) {
- return formatMeasure(value, metric.type, options);
- } else {
- return formatMeasureVariation(value, metric.type, options);
- }
-}
-
-export function enhanceWithLeak(measures, periodIndex = 1) {
- function enhanceSingle(measure) {
- return { ...measure, leak: getLeakValue(measure, periodIndex) };
- }
-
- if (Array.isArray(measures)) {
- return measures.map(enhanceSingle);
- } else {
- return enhanceSingle(measures);
- }
-}
-
-export function enhanceWithSingleMeasure(components, periodIndex = 1) {
- return components.map(component => {
- return {
- ...component,
- value: getSingleMeasureValue(component.measures),
- leak: getSingleLeakValue(component.measures, periodIndex)
- };
- });
-}
-
-export function enhanceWithMeasure(components, metric, periodIndex = 1) {
- return components.map(component => {
- const measuresWithLeak = enhanceWithLeak(component.measures, periodIndex);
- const measure = measuresWithLeak.find(measure => measure.metric === metric);
- const value = measure ? measure.value : null;
- const leak = measure ? measure.leak : null;
- return { ...component, value, leak, measures: measuresWithLeak };
- });
-}
-
-export function hasBubbleChart(domainName) {
- return !!bubbles[domainName];
-}
-
-export function hasTreemap(metric) {
- return ['PERCENT', 'RATING', 'LEVEL'].indexOf(metric.type) !== -1;
-}
-
-export function filterOutEmptyMeasures(components) {
- return components.filter(component => component.value !== null || component.leak !== null);
-}
-
-export function getRatingTooltip(metricKey, value) {
- const finalMetricKey = metricKey.indexOf('new_') === 0 ? metricKey.substr(4) : metricKey;
- const KNOWN_RATINGS = ['sqale_rating', 'reliability_rating', 'security_rating'];
- if (KNOWN_RATINGS.includes(finalMetricKey)) {
- return nextGetRatingTooltip(finalMetricKey, value);
- }
- return null;
-}
renderHeader() {
const { component } = this.props;
const bugsDomainUrl = {
- pathname: '/component_measures/domain/Reliability',
+ pathname: '/component_measures_old/domain/Reliability',
query: { id: component.key }
};
const vulnerabilitiesDomainUrl = {
- pathname: '/component_measures/domain/Security',
+ pathname: '/component_measures_old/domain/Security',
query: { id: component.key }
};
renderHeader = (domain, label) => {
const { component } = this.props;
const domainUrl = {
- pathname: `/component_measures/domain/${domain}`,
+ pathname: `/component_measures_old/domain/${domain}`,
query: { id: component.key }
};
import classNames from 'classnames';
import { Link } from 'react-router';
import { DrilldownLink } from '../../../components/shared/drilldown-link';
-import Measure from '../../component-measures/components/Measure';
+import Measure from '../../component-measures-old/components/Measure';
import { getPeriodValue, isDiffMetric, formatMeasure } from '../../../helpers/measures';
import { translate } from '../../../helpers/l10n';
import { getComponentIssuesUrl } from '../../../helpers/urls';
*/
//@flow
import React from 'react';
-import Measure from '../../component-measures/components/Measure';
+import Measure from '../../component-measures-old/components/Measure';
import BugIcon from '../../../components/icons-components/BugIcon';
import CodeSmellIcon from '../../../components/icons-components/CodeSmellIcon';
import Rating from '../../../components/ui/Rating';
//@flow
import React from 'react';
import ProjectCardLanguages from './ProjectCardLanguages';
-import Measure from '../../component-measures/components/Measure';
+import Measure from '../../component-measures-old/components/Measure';
import Rating from '../../../components/ui/Rating';
import CoverageRating from '../../../components/ui/CoverageRating';
import DuplicationsRating from '../../../components/ui/DuplicationsRating';
describe('#getComponentDrilldownUrl', () => {
it('should return component drilldown url', () => {
expect(getComponentDrilldownUrl(SIMPLE_COMPONENT_KEY, METRIC)).toEqual({
- pathname: '/component_measures/metric/' + METRIC,
+ pathname: '/component_measures_old/metric/' + METRIC,
query: { id: SIMPLE_COMPONENT_KEY }
});
});
it('should not encode component key', () => {
expect(getComponentDrilldownUrl(COMPLEX_COMPONENT_KEY, METRIC)).toEqual({
- pathname: '/component_measures/metric/' + METRIC,
+ pathname: '/component_measures_old/metric/' + METRIC,
query: { id: COMPLEX_COMPONENT_KEY }
});
});
*/
export function getComponentDrilldownUrl(componentKey, metric) {
return {
- pathname: `/component_measures/metric/${metric}`,
+ pathname: `/component_measures_old/metric/${metric}`,
query: { id: componentKey }
};
}
import organizations, * as fromOrganizations from './organizations/duck';
import organizationsMembers, * as fromOrganizationsMembers from './organizationsMembers/reducer';
import globalMessages, * as fromGlobalMessages from './globalMessages/duck';
-import measuresApp, * as fromMeasuresApp from '../apps/component-measures/store/rootReducer';
+import measuresApp, * as fromMeasuresApp from '../apps/component-measures-old/store/rootReducer';
import permissionsApp, * as fromPermissionsApp from '../apps/permissions/shared/store/rootReducer';
import projectAdminApp, * as fromProjectAdminApp from '../apps/project-admin/store/rootReducer';
import projectsApp, * as fromProjectsApp from '../apps/projects/store/reducer';