@@ -67,6 +67,7 @@ import { | |||
import { translate, translateWithParameters } from '../../../helpers/l10n'; | |||
import { RawQuery } from '../../../helpers/query'; | |||
import { scrollToElement } from '../../../helpers/scrolling'; | |||
import EmptySearch from '../../../components/common/EmptySearch'; | |||
import Checkbox from '../../../components/controls/Checkbox'; | |||
import '../styles.css'; | |||
@@ -926,6 +927,17 @@ export default class App extends React.PureComponent<Props, State> { | |||
return null; | |||
} | |||
let noIssuesMessage = null; | |||
if (paging.total === 0) { | |||
if (this.isFiltered()) { | |||
noIssuesMessage = <EmptySearch />; | |||
} else if (this.state.myIssues) { | |||
noIssuesMessage = <NoMyIssues />; | |||
} else { | |||
noIssuesMessage = <NoIssues />; | |||
} | |||
} | |||
return ( | |||
<div> | |||
{paging.total > 0 && ( | |||
@@ -949,8 +961,7 @@ export default class App extends React.PureComponent<Props, State> { | |||
<ListFooter count={issues.length} loadMore={this.fetchMoreIssues} total={paging.total} /> | |||
)} | |||
{paging.total === 0 && | |||
(this.state.myIssues && !this.isFiltered() ? <NoMyIssues /> : <NoIssues />)} | |||
{noIssuesMessage} | |||
</div> | |||
); | |||
} |
@@ -45,6 +45,7 @@ import { isSameBranchLike, getBranchLikeQuery } from '../../../helpers/branches' | |||
import { fetchMetrics } from '../../../store/rootActions'; | |||
import { getMetrics } from '../../../store/rootReducer'; | |||
import { BranchLike, Component, Metric } from '../../../app/types'; | |||
import { translate } from '../../../helpers/l10n'; | |||
import '../styles.css'; | |||
interface OwnProps { | |||
@@ -158,6 +159,10 @@ export class OverviewApp extends React.PureComponent<Props, State> { | |||
? { index: 1 } | |||
: undefined; | |||
isEmpty = () => | |||
this.state.measures === undefined || | |||
this.state.measures.find(measure => measure.metric.key === 'ncloc') === undefined; | |||
renderLoading() { | |||
return ( | |||
<div className="text-center"> | |||
@@ -166,14 +171,22 @@ export class OverviewApp extends React.PureComponent<Props, State> { | |||
); | |||
} | |||
render() { | |||
const { branchLike, component } = this.props; | |||
const { loading, measures, periods, history, historyStartDate } = this.state; | |||
if (loading) { | |||
return this.renderLoading(); | |||
} | |||
renderEmpty() { | |||
return ( | |||
<div className="overview-main page-main"> | |||
<h3> | |||
{!this.state.measures || | |||
!this.state.measures.find(measure => measure.metric.key === 'projects') | |||
? translate('portfolio.app.empty') | |||
: translate('portfolio.app.no_lines_of_code')} | |||
</h3> | |||
</div> | |||
); | |||
} | |||
renderMain() { | |||
const { branchLike, component } = this.props; | |||
const { periods, measures, history, historyStartDate } = this.state; | |||
const leakPeriod = | |||
component.qualifier === 'APP' ? this.getApplicationLeakPeriod() : getLeakPeriod(periods); | |||
const domainProps = { | |||
@@ -185,23 +198,40 @@ export class OverviewApp extends React.PureComponent<Props, State> { | |||
historyStartDate | |||
}; | |||
if (this.isEmpty()) { | |||
return this.renderEmpty(); | |||
} | |||
return ( | |||
<div className="overview-main page-main"> | |||
{component.qualifier === 'APP' ? ( | |||
<ApplicationQualityGate component={component} /> | |||
) : ( | |||
<QualityGate branchLike={branchLike} component={component} measures={measures} /> | |||
)} | |||
<div className="overview-domains-list"> | |||
<BugsAndVulnerabilities {...domainProps} /> | |||
<CodeSmells {...domainProps} /> | |||
<Coverage {...domainProps} /> | |||
<Duplications {...domainProps} /> | |||
</div> | |||
</div> | |||
); | |||
} | |||
render() { | |||
const { branchLike, component } = this.props; | |||
const { loading, measures, history } = this.state; | |||
if (loading) { | |||
return this.renderLoading(); | |||
} | |||
return ( | |||
<div className="page page-limited"> | |||
<div className="overview page-with-sidebar"> | |||
<div className="overview-main page-main"> | |||
{component.qualifier === 'APP' ? ( | |||
<ApplicationQualityGate component={component} /> | |||
) : ( | |||
<QualityGate branchLike={branchLike} component={component} measures={measures} /> | |||
)} | |||
<div className="overview-domains-list"> | |||
<BugsAndVulnerabilities {...domainProps} /> | |||
<CodeSmells {...domainProps} /> | |||
<Coverage {...domainProps} /> | |||
<Duplications {...domainProps} /> | |||
</div> | |||
</div> | |||
{this.renderMain()} | |||
<div className="overview-sidebar page-sidebar-fixed"> | |||
<Meta |
@@ -34,21 +34,27 @@ interface Props { | |||
} | |||
export default class MetaSize extends React.PureComponent<Props> { | |||
renderLoC = (ncloc: MeasureEnhanced) => ( | |||
renderLoC = (ncloc?: MeasureEnhanced) => ( | |||
<div | |||
id="overview-ncloc" | |||
className={classNames('overview-meta-size-ncloc', { | |||
'is-half-width': this.props.component.qualifier === 'APP' | |||
})}> | |||
<span className="spacer-right"> | |||
<SizeRating value={Number(ncloc.value)} /> | |||
</span> | |||
<DrilldownLink | |||
branchLike={this.props.branchLike} | |||
component={this.props.component.key} | |||
metric="ncloc"> | |||
{formatMeasure(ncloc.value, 'SHORT_INT')} | |||
</DrilldownLink> | |||
})} | |||
id="overview-ncloc"> | |||
{ncloc && ( | |||
<span className="spacer-right"> | |||
<SizeRating value={Number(ncloc.value)} /> | |||
</span> | |||
)} | |||
{ncloc ? ( | |||
<DrilldownLink | |||
branchLike={this.props.branchLike} | |||
component={this.props.component.key} | |||
metric="ncloc"> | |||
{formatMeasure(ncloc.value, 'SHORT_INT')} | |||
</DrilldownLink> | |||
) : ( | |||
<span>0</span> | |||
)} | |||
<div className="spacer-top text-muted">{getMetricName('ncloc')}</div> | |||
</div> | |||
); | |||
@@ -70,24 +76,27 @@ export default class MetaSize extends React.PureComponent<Props> { | |||
renderProjects = () => { | |||
const projects = this.props.measures.find(measure => measure.metric.key === 'projects'); | |||
return projects ? ( | |||
<div id="overview-projects" className="overview-meta-size-ncloc is-half-width"> | |||
<DrilldownLink | |||
branchLike={this.props.branchLike} | |||
component={this.props.component.key} | |||
metric="projects"> | |||
{formatMeasure(projects.value, 'SHORT_INT')} | |||
</DrilldownLink> | |||
return ( | |||
<div className="overview-meta-size-ncloc is-half-width" id="overview-projects"> | |||
{projects ? ( | |||
<DrilldownLink | |||
branchLike={this.props.branchLike} | |||
component={this.props.component.key} | |||
metric="projects"> | |||
{formatMeasure(projects.value, 'SHORT_INT')} | |||
</DrilldownLink> | |||
) : ( | |||
<span>0</span> | |||
)} | |||
<div className="spacer-top text-muted">{translate('metric.projects.name')}</div> | |||
</div> | |||
) : null; | |||
); | |||
}; | |||
render() { | |||
const ncloc = this.props.measures.find(measure => measure.metric.key === 'ncloc'); | |||
if (ncloc == null) { | |||
if (ncloc == null && this.props.component.qualifier !== 'APP') { | |||
return null; | |||
} | |||
@@ -363,7 +363,8 @@ | |||
box-sizing: border-box; | |||
} | |||
.overview-meta-size-ncloc a { | |||
.overview-meta-size-ncloc a, | |||
.overview-meta-size-ncloc span { | |||
line-height: var(--controlHeight); | |||
font-size: 18px; | |||
font-weight: 300; |
@@ -123,8 +123,11 @@ export class App extends React.PureComponent<Props, State> { | |||
renderEmpty() { | |||
return ( | |||
<div className="empty-search"> | |||
<h3>{translate('portfolio.empty')}</h3> | |||
<p>{translate('portfolio.empty.hint')}</p> | |||
<h3> | |||
{!this.state.measures || !this.state.measures['projects'] | |||
? translate('portfolio.empty') | |||
: translate('portfolio.no_lines_of_code')} | |||
</h3> | |||
</div> | |||
); | |||
} | |||
@@ -188,8 +191,7 @@ export class App extends React.PureComponent<Props, State> { | |||
<h4 className="portfolio-meta-header"> | |||
{translate('overview.about_this_portfolio')} | |||
</h4> | |||
{!this.isEmpty() && | |||
!this.isNotComputed() && <Summary component={component} measures={measures!} />} | |||
<Summary component={component} measures={measures || {}} /> | |||
</div> | |||
<div className="portfolio-meta-card"> |
@@ -33,9 +33,11 @@ export default function RatingFreshness({ lastChange, rating }: Props) { | |||
return ( | |||
<div className="portfolio-freshness"> | |||
{rating && ( | |||
<> | |||
{translate('portfolio.has_always_been')} <Rating small={true} value={rating} /> | |||
</> | |||
<FormattedMessage | |||
defaultMessage={translate('portfolio.has_always_been_x')} | |||
id="portfolio.has_always_been_x" | |||
values={{ rating: <Rating small={true} value={rating} /> }} | |||
/> | |||
)} | |||
</div> | |||
); |
@@ -40,17 +40,25 @@ export default function Summary({ component, measures }: Props) { | |||
<ul className="portfolio-grid"> | |||
<li> | |||
<div className="portfolio-measure-secondary-value"> | |||
<Link to={getComponentDrilldownUrl(component.key, 'projects')}> | |||
<Measure metricKey="projects" metricType="SHORT_INT" value={projects} /> | |||
</Link> | |||
{projects ? ( | |||
<Link to={getComponentDrilldownUrl(component.key, 'projects')}> | |||
<Measure metricKey="projects" metricType="SHORT_INT" value={projects} /> | |||
</Link> | |||
) : ( | |||
'0' | |||
)} | |||
</div> | |||
<div className="spacer-top text-muted">{translate('projects')}</div> | |||
</li> | |||
<li> | |||
<div className="portfolio-measure-secondary-value"> | |||
<Link to={getComponentDrilldownUrl(component.key, 'ncloc')}> | |||
<Measure metricKey="ncloc" metricType="SHORT_INT" value={ncloc} /> | |||
</Link> | |||
{ncloc ? ( | |||
<Link to={getComponentDrilldownUrl(component.key, 'ncloc')}> | |||
<Measure metricKey="ncloc" metricType="SHORT_INT" value={ncloc} /> | |||
</Link> | |||
) : ( | |||
'0' | |||
)} | |||
</div> | |||
<div className="spacer-top text-muted">{translate('metric.ncloc.name')}</div> | |||
</li> |
@@ -127,9 +127,6 @@ exports[`renders when portfolio is empty 1`] = ` | |||
<h3> | |||
portfolio.empty | |||
</h3> | |||
<p> | |||
portfolio.empty.hint | |||
</p> | |||
</div> | |||
</div> | |||
<aside | |||
@@ -143,6 +140,20 @@ exports[`renders when portfolio is empty 1`] = ` | |||
> | |||
overview.about_this_portfolio | |||
</h4> | |||
<Summary | |||
component={ | |||
Object { | |||
"key": "foo", | |||
"name": "Foo", | |||
"qualifier": "TRK", | |||
} | |||
} | |||
measures={ | |||
Object { | |||
"reliability_rating": "1", | |||
} | |||
} | |||
/> | |||
</div> | |||
<div | |||
className="portfolio-meta-card" | |||
@@ -199,6 +210,20 @@ exports[`renders when portfolio is not computed 1`] = ` | |||
> | |||
overview.about_this_portfolio | |||
</h4> | |||
<Summary | |||
component={ | |||
Object { | |||
"key": "foo", | |||
"name": "Foo", | |||
"qualifier": "TRK", | |||
} | |||
} | |||
measures={ | |||
Object { | |||
"ncloc": "173", | |||
} | |||
} | |||
/> | |||
</div> | |||
<div | |||
className="portfolio-meta-card" |
@@ -32,13 +32,17 @@ exports[`renders has always been 1`] = ` | |||
<div | |||
className="portfolio-freshness" | |||
> | |||
<React.Fragment> | |||
portfolio.has_always_been | |||
<Rating | |||
small={true} | |||
value="A" | |||
/> | |||
</React.Fragment> | |||
<FormattedMessage | |||
defaultMessage="portfolio.has_always_been_x" | |||
id="portfolio.has_always_been_x" | |||
values={ | |||
Object { | |||
"rating": <Rating | |||
small={true} | |||
value="A" | |||
/>, | |||
} | |||
} | |||
/> | |||
</div> | |||
`; |
@@ -2678,12 +2678,14 @@ branches.see_the_pr=See the PR | |||
# PORTFOLIOS | |||
# | |||
#------------------------------------------------------------------------------ | |||
portfolio.has_always_been=has always been | |||
portfolio.has_always_been_x=has always been {rating} | |||
portfolio.was_x_y=was {rating} {date} | |||
portfolio.x_in_y={projects} in {rating} | |||
portfolio.empty=This portfolio is empty. | |||
portfolio.empty.hint=This portfolio has no projects, or none of associated projects has lines of code. | |||
portfolio.no_lines_of_code=All projects in this portfolio are empty | |||
portfolio.not_computed=This portfolio is not yet computed. | |||
portfolio.app.empty=This application is empty. | |||
portfolio.app.no_lines_of_code=All projects in this application are empty | |||
#------------------------------------------------------------------------------ |