import $ from 'jquery';
import _ from 'underscore';
import React from 'react';
-import Main from './main';
+import { Overview } from './main';
class App {
start (options) {
_.extend(opts.component, options.component);
$('html').toggleClass('dashboard-page', opts.component.hasSnapshot);
let el = document.querySelector(opts.el);
- React.render(<Main {...opts}/>, el);
+ React.render(<Overview {...opts}/>, el);
}
}
import React from 'react';
+
import Measure from './../helpers/measure';
import { periodLabel, getPeriodDate } from './../helpers/period-label';
import DrilldownLink from './../helpers/drilldown-link';
+
export default React.createClass({
render() {
let metricName = window.t('metric', this.props.condition.metric.name, 'name'),
threshold = this.props.condition.level === 'ERROR' ?
- this.props.condition.error : this.props.condition.warning,
- iconClassName = 'icon-alert-' + this.props.condition.level.toLowerCase(),
+ this.props.condition.error : this.props.condition.warning,
period = this.props.condition.period ?
- `(${periodLabel(this.props.component.periods, this.props.condition.period)})` : null,
+ `(${periodLabel(this.props.component.periods, this.props.condition.period)})` : null,
periodDate = getPeriodDate(this.props.component.periods, this.props.condition.period);
+ let classes = 'alert_' + this.props.condition.level.toUpperCase();
+
return (
<div>
<h4 className="overview-gate-condition-metric">{metricName}<br/><span className="nowrap">{period}</span></h4>
<div className="overview-gate-condition-value">
- <i className={iconClassName}/>
<DrilldownLink component={this.props.component.key} metric={this.props.condition.metric.name}
period={this.props.condition.period} periodDate={periodDate}>
- <Measure value={this.props.condition.actual} type={this.props.condition.metric.type}/>
+ <span className={classes}>
+ <Measure value={this.props.condition.actual} type={this.props.condition.metric.type}/>
+ </span>
</DrilldownLink>
<span className="overview-gate-condition-itself">
{window.t('quality_gates.operator', this.props.condition.op, 'short')}
<Donut data={donutData} size="47"/>
</div>
<div className="measure measure-big" data-metric="new_coverage">
+ <span className="measure-name">{window.t('overview.metric.new_coverage')}</span>
<span className="measure-value">
- <DrilldownLink component={this.props.component.key} metric="new_coverage" period="3">
+ <DrilldownLink component={this.props.component.key} metric="new_coverage" period="1">
<Measure value={newCoverage} type="PERCENT"/>
</DrilldownLink>
</span>
- <span className="measure-name">{window.t('overview.metric.new_coverage')}</span>
</div>
</div>
<ul className="list-inline big-spacer-top measures-chart-indent">
<Donut data={donutData} size="47"/>
</div>
<div className="measure measure-big" data-metric="duplicated_lines_density">
+ <span className="measure-name">{window.t('overview.metric.duplications')}</span>
<span className="measure-value">
<MeasureVariation value={density} type="PERCENT"/>
</span>
- <span className="measure-name">{window.t('overview.metric.duplications')}</span>
</div>
</div>
<ul className="list-inline big-spacer-top measures-chart-indent">
blockerIssues = this.props.leak.newBlockerIssues,
criticalIssues = this.props.leak.newCriticalIssues,
issuesToReview = this.props.leak.newOpenIssues + this.props.leak.newReopenedIssues,
- periodDate = moment(getPeriodDate(this.props.component.periods, '3')).format('YYYY-MM-DDTHH:mm:ssZZ');
+ periodDate = moment(getPeriodDate(this.props.component.periods, '1')).format('YYYY-MM-DDTHH:mm:ssZZ');
return (
<Card>
<div className="measures">
<div className="measure measure-big" data-metric="sqale_index">
+ <span className="measure-name">{window.t('overview.metric.new_debt')}</span>
<span className="measure-value">
<IssuesLink component={this.props.component.key}
params={{ resolved: 'false', createdAfter: periodDate, facetMode: 'debt' }}>
<Measure value={newDebt} type="SHORT_WORK_DUR"/>
</IssuesLink>
</span>
- <span className="measure-name">{window.t('overview.metric.new_debt')}</span>
</div>
<div className="measure measure-big" data-metric="violations">
+ <span className="measure-name">{window.t('overview.metric.new_issues')}</span>
<span className="measure-value">
<IssuesLink component={this.props.component.key}
params={{ resolved: 'false', createdAfter: periodDate }}>
<Measure value={issues} type="SHORT_INT"/>
</IssuesLink>
</span>
- <span className="measure-name">{window.t('overview.metric.new_issues')}</span>
</div>
</div>
<ul className="list-inline big-spacer-top">
<Card>
<div className="measures">
<div className="measure measure-big" data-metric="lines">
+ <span className="measure-name">{window.t('overview.metric.lines')}</span>
<span className="measure-value">
<MeasureVariation value={lines} type="SHORT_INT"/>
</span>
- <span className="measure-name">{window.t('overview.metric.lines')}</span>
</div>
<div className="measure measure-big" data-metric="files">
+ <span className="measure-name">{window.t('overview.metric.files')}</span>
<span className="measure-value">
<MeasureVariation value={files} type="SHORT_INT"/>
</span>
- <span className="measure-name">{window.t('overview.metric.files')}</span>
</div>
</div>
</Card>
import _ from 'underscore';
+import moment from 'moment';
import React from 'react';
+
import Cards from './cards';
import LeakIssues from './leak-issues';
import LeakCoverage from './leak-coverage';
import LeakSize from './leak-size';
import LeakDups from './leak-dups';
-import {periodLabel} from './../helpers/period-label';
+import { periodLabel, getPeriodDate } from './../helpers/period-label';
+
export default React.createClass({
render() {
- if (_.size(this.props.component.periods) < 3) {
+ if (_.size(this.props.component.periods) < 1) {
return null;
}
- let period = periodLabel(this.props.component.periods, '3');
+ let period = periodLabel(this.props.component.periods, '1');
+ let periodDate = getPeriodDate(this.props.component.periods, '1');
return (
<div className="overview-leak">
<h2 className="overview-title">
{window.t('overview.water_leak')}
- <span className="overview-leak-period">{period}</span>
+ <span className="overview-leak-period">{period} / {moment(periodDate).format('LL')}</span>
</h2>
<Cards>
<LeakIssues component={this.props.component} leak={this.props.leak} measures={this.props.measures}/>
},
_hasWaterLeak() {
- return !!_.findWhere(this.props.component.periods, { index: '3' });
+ return !!_.findWhere(this.props.component.periods, { index: '1' });
},
_requestIssues(data) {
},
requestLeakIssues() {
- let createdAfter = moment(getPeriodDate(this.props.component.periods, '3')).format('YYYY-MM-DDTHH:mm:ssZZ');
+ let createdAfter = moment(getPeriodDate(this.props.component.periods, '1')).format('YYYY-MM-DDTHH:mm:ssZZ');
this._requestIssues({ resolved: 'false', createdAfter, facets: 'severities,statuses' }).done(r => {
let
severitiesFacet = _.findWhere(r.facets, { property: 'severities' }).values,
},
requestLeakDebt() {
- let createdAfter = moment(getPeriodDate(this.props.component.periods, '3')).format('YYYY-MM-DDTHH:mm:ssZZ');
+ let createdAfter = moment(getPeriodDate(this.props.component.periods, '1')).format('YYYY-MM-DDTHH:mm:ssZZ');
this._requestIssues({ resolved: 'false', createdAfter, facets: 'severities', facetMode: 'debt' }).done(r => {
this.setState({
leak: _.extend({}, this.state.leak, { newDebt: r.debtTotal })
<Donut data={donutData} size="47"/>
</div>
<div className="measure measure-big">
+ <span className="measure-name">{window.t('overview.metric.coverage')}</span>
<span className="measure-value">
<DrilldownLink component={this.props.component.key} metric="overall_coverage">
<Measure value={coverage} type="PERCENT"/>
</DrilldownLink>
</span>
- <span className="measure-name">{window.t('overview.metric.coverage')}</span>
</div>
</div>
<ul className="list-inline big-spacer-top measures-chart-indent">
<Donut data={donutData} size="47"/>
</div>
<div className="measure measure-big">
+ <span className="measure-name">{window.t('overview.metric.duplications')}</span>
<span className="measure-value">
<DrilldownLink component={this.props.component.key} metric="duplicated_lines_density">
<Measure value={density} type="PERCENT"/>
</DrilldownLink>
</span>
- <span className="measure-name">{window.t('overview.metric.duplications')}</span>
</div>
</div>
<ul className="list-inline big-spacer-top measures-chart-indent">
</DrilldownLink>
</div>
<div className="measure measure-big" data-metric="sqale_index">
+ <span className="measure-name">{window.t('overview.metric.debt')}</span>
<span className="measure-value">
<IssuesLink component={this.props.component.key} params={{ resolved: 'false', facetMode: 'debt' }}>
<Measure value={debt} type="SHORT_WORK_DUR"/>
</IssuesLink>
</span>
- <span className="measure-name">{window.t('overview.metric.debt')}</span>
</div>
<div className="measure measure-big" data-metric="violations">
+ <span className="measure-name">{window.t('overview.metric.issues')}</span>
<span className="measure-value">
<IssuesLink component={this.props.component.key} params={{ resolved: 'false' }}>
<Measure value={issues} type="SHORT_INT"/>
</IssuesLink>
</span>
- <span className="measure-name">{window.t('overview.metric.issues')}</span>
</div>
</div>
<ul className="list-inline big-spacer-top">
<Card linkTo="size" active={active} onRoute={this.props.onRoute}>
<div className="measures">
<div className="measure measure-big" data-metric="lines">
+ <span className="measure-name">{window.t('overview.metric.lines')}</span>
<span className="measure-value">
<DrilldownLink component={this.props.component.key} metric="lines">
<Measure value={lines} type="SHORT_INT"/>
</DrilldownLink>
</span>
- <span className="measure-name">{window.t('overview.metric.lines')}</span>
</div>
<div className="measure measure-big" data-metric="files">
+ <span className="measure-name">{window.t('overview.metric.files')}</span>
<span className="measure-value">
<DrilldownLink component={this.props.component.key} metric="files">
<Measure value={files} type="SHORT_INT"/>
</DrilldownLink>
</span>
- <span className="measure-name">{window.t('overview.metric.files')}</span>
</div>
</div>
</Card>
import { getMetrics } from '../../api/metrics';
-export default class Overview extends React.Component {
- constructor () {
- super();
+
+export const Overview = React.createClass({
+ getInitialState () {
let hash = window.location.hash;
- this.state = { section: hash.length ? hash.substr(1) : null };
- }
+ return { section: hash.length ? hash.substr(1) : null };
+ },
+
+ componentWillMount () {
+ window.addEventListener('hashchange', this.handleHashChange);
+ },
componentDidMount () {
this.requestMetrics();
- }
+ },
+
+ componentWillUnmount () {
+ window.removeEventListener('hashchange', this.handleHashChange);
+ },
requestMetrics () {
return getMetrics().then(metrics => this.setState({ metrics }));
- }
+ },
handleRoute (section, el) {
- this.setState({ section }, () => this.scrollToEl(el));
- window.location.href = '#' + section;
- }
+ if (section !== this.state.section) {
+ this.setState({ section }, () => this.scrollToEl(el));
+ window.location.href = '#' + section;
+ } else {
+ this.setState({ section: null });
+ window.location.href = '#';
+ }
+ },
+
+ handleHashChange () {
+ let hash = window.location.hash;
+ this.setState({ section: hash.substr(1) });
+ },
scrollToEl (el) {
let top = offset(el).top - el.getBoundingClientRect().height;
window.scrollTo(0, top);
- }
+ },
render () {
if (!this.state.metrics) {
return <div className="overview">
<div className="overview-main">
- <GeneralMain {...this.props} section={this.state.section} onRoute={this.handleRoute.bind(this)}/>
+ <GeneralMain {...this.props} section={this.state.section} onRoute={this.handleRoute}/>
{child}
</div>
<Meta component={this.props.component}/>
</div>;
}
-}
+});
fontSize: SIZE_SCALE(this.props.width / this.props.label.length),
lineHeight: `${this.props.height}px`
};
+ let isTextVisible = this.props.width >= 40 && this.props.height >= 40;
return <div className="treemap-cell" {...tooltipAttrs} style={cellStyles}>
<div className="treemap-inner" dangerouslySetInnerHTML={{ __html: this.props.label }}
- style={{ maxWidth: this.props.width }}/>
+ style={{ maxWidth: this.props.width, visibility: isTextVisible ? 'visible': 'hidden' }}/>
</div>;
}
});
.size([this.state.width, 360]);
let nodes = treemap
.nodes({ children: this.props.items })
- .filter(d => !d.children);
+ .filter(d => !d.children)
+ .filter(d => !!d.dx && !!d.dy);
let prefix = mostCommitPrefix(this.props.items.map(item => item.label)),
prefixLength = prefix.length;
.overview-gate {
.clearfix;
- padding: 50px 30px;
+ padding: 50px 0 25px;
}
.overview-gate-box {
margin-top: 8px;
font-weight: 300;
font-size: 22px;
-
- i {
- position: relative;
- top: -1px;
- }
}
.overview-gate-condition-itself {
}
.overview-leak {
- padding: 50px 30px;
+ padding: 50px 0 25px;
border-top: 1px solid @barBorderColor;
border-bottom: 1px solid @barBorderColor;
}
.overview-title {
- margin-bottom: 20px;
+ padding: 0 30px;
font-size: 18px;
font-weight: 400;
}
.overview-nutshell {
- padding: 50px 30px 0;
-
- .overview-card {
- padding-bottom: 25px;
- }
+ padding: 50px 0 0;
}
.overview-cards {
.overview-card {
flex: 1 0 25%;
+ padding: 25px 30px;
box-sizing: border-box;
.overview-gate & {
margin-left: 30px;
}
+ .measure-big .measure-name {
+ margin-top: 0;
+ margin-bottom: 2px;
+ }
+
.list-inline {
margin-left: -10px;
margin-right: -10px;
}
.overview-card-section {
+ position: relative;
+ z-index: 100;
cursor: pointer;
&:hover, &.active {
- border-bottom: 4px solid #2c3946;
+ &:before {
+ position: absolute;
+ top: 100%;
+ left: 50%;
+ margin-left: -10px;
+ width: 0;
+ height: 0;
+ border: solid transparent;
+ border-width: 10px;
+ border-top-color: #fff;
+ content: '';
+ pointer-events: none;
+ }
}
}
.overview-meta {
width: 240px;
- padding: 50px 30px;
border-left: 1px solid @barBorderColor;
box-sizing: border-box;
background-color: @barBackgroundColor;
.overview-meta .overview-card {
width: auto;
- margin-bottom: 30px;
}
.overview-meta-description {
.overview-domain-section {
padding: 50px 30px;
+
+ .overview-title {
+ margin-bottom: 25px;
+ padding-left: 0;
+ padding-right: 0;
+ }
}
.overview-domain-header {
<% if @snapshot %>
// coverage
<% if @snapshot.measure('new_overall_coverage') %>
- newCoverage: '<%= @snapshot.measure('new_overall_coverage').variation(3) -%>',
+ newCoverage: '<%= @snapshot.measure('new_overall_coverage').variation(1) -%>',
<% end %>
<% if @snapshot.measure('tests') %>
- tests: '<%= @snapshot.measure('tests').variation(3) -%>',
+ tests: '<%= @snapshot.measure('tests').variation(1) -%>',
<% end %>
// duplications
- duplications: '<%= @snapshot.measure('duplicated_lines_density').variation(3) -%>',
- duplicatedLines: '<%= @snapshot.measure('duplicated_lines').variation(3) -%>',
- duplicatedBlocks: '<%= @snapshot.measure('duplicated_blocks').variation(3) -%>',
+ duplications: '<%= @snapshot.measure('duplicated_lines_density').variation(1) -%>',
+ duplicatedLines: '<%= @snapshot.measure('duplicated_lines').variation(1) -%>',
+ duplicatedBlocks: '<%= @snapshot.measure('duplicated_blocks').variation(1) -%>',
// size
- lines: '<%= @snapshot.measure('lines').variation(3) -%>',
- files: '<%= @snapshot.measure('files').variation(3) -%>'
+ lines: '<%= @snapshot.measure('lines').variation(1) -%>',
+ files: '<%= @snapshot.measure('files').variation(1) -%>'
<% end %>
};