contextNavHeightRaw: 9 * grid, | contextNavHeightRaw: 9 * grid, | ||||
pagePadding: '20px', | |||||
// different | // different | ||||
defaultShadow: '0 6px 12px rgba(0, 0, 0, 0.175)', | defaultShadow: '0 6px 12px rgba(0, 0, 0, 0.175)', | ||||
const header = translate('overview.badges.title'); | const header = translate('overview.badges.title'); | ||||
const fullBadgeOptions = { branch, project, ...badgeOptions }; | const fullBadgeOptions = { branch, project, ...badgeOptions }; | ||||
return ( | return ( | ||||
<> | |||||
<div className="overview-meta-card"> | |||||
<button className="js-project-badges" onClick={this.handleOpen}> | <button className="js-project-badges" onClick={this.handleOpen}> | ||||
{translate('overview.badges.get_badge')} | {translate('overview.badges.get_badge')} | ||||
</button> | </button> | ||||
</footer> | </footer> | ||||
</Modal> | </Modal> | ||||
)} | )} | ||||
</> | |||||
</div> | |||||
); | ); | ||||
} | } | ||||
} | } |
// Jest Snapshot v1, https://goo.gl/fbAQLP | // Jest Snapshot v1, https://goo.gl/fbAQLP | ||||
exports[`should display the modal after click 1`] = ` | exports[`should display the modal after click 1`] = ` | ||||
<React.Fragment> | |||||
<div | |||||
className="overview-meta-card" | |||||
> | |||||
<button | <button | ||||
className="js-project-badges" | className="js-project-badges" | ||||
onClick={[Function]} | onClick={[Function]} | ||||
> | > | ||||
overview.badges.get_badge | overview.badges.get_badge | ||||
</button> | </button> | ||||
</React.Fragment> | |||||
</div> | |||||
`; | `; | ||||
exports[`should display the modal after click 2`] = ` | exports[`should display the modal after click 2`] = ` |
</div> | </div> | ||||
</div> | </div> | ||||
<div className="page-sidebar-fixed"> | |||||
<div className="overview-sidebar page-sidebar-fixed"> | |||||
<Meta | <Meta | ||||
branch={branchName} | branch={branchName} | ||||
component={component} | component={component} |
return ( | return ( | ||||
<div className="overview-meta-card"> | <div className="overview-meta-card"> | ||||
<h4 className="overview-meta-header">{translate('project_activity.page')}</h4> | |||||
<h4 className="overview-meta-header"> | |||||
{translate('overview.project_activity', this.props.component.qualifier)} | |||||
</h4> | |||||
<PreviewGraph | <PreviewGraph | ||||
branch={this.props.branch} | branch={this.props.branch} |
import AnalysesList from '../events/AnalysesList'; | import AnalysesList from '../events/AnalysesList'; | ||||
import { Visibility, Component, Metric } from '../../../app/types'; | import { Visibility, Component, Metric } from '../../../app/types'; | ||||
import { History } from '../../../api/time-machine'; | import { History } from '../../../api/time-machine'; | ||||
import { translate } from '../../../helpers/l10n'; | |||||
import { MeasureEnhanced } from '../../../helpers/measures'; | import { MeasureEnhanced } from '../../../helpers/measures'; | ||||
interface Props { | interface Props { | ||||
const isProject = qualifier === 'TRK'; | const isProject = qualifier === 'TRK'; | ||||
const isPrivate = visibility === Visibility.Private; | const isPrivate = visibility === Visibility.Private; | ||||
const hasDescription = !!description; | |||||
const hasQualityProfiles = Array.isArray(qualityProfiles) && qualityProfiles.length > 0; | |||||
const hasQualityGate = !!qualityGate; | |||||
const shouldShowQualityProfiles = isProject && hasQualityProfiles; | |||||
const shouldShowQualityGate = isProject && hasQualityGate; | |||||
const hasOrganization = component.organization != null && organizationsEnabled; | |||||
return ( | return ( | ||||
<div className="overview-meta"> | <div className="overview-meta"> | ||||
{hasDescription && ( | |||||
<div className="overview-meta-card overview-meta-description">{description}</div> | |||||
)} | |||||
<MetaSize branch={branch} component={component} measures={this.props.measures} /> | |||||
{isProject && ( | |||||
<MetaTags component={component} onComponentChange={this.props.onComponentChange} /> | |||||
)} | |||||
<div className="overview-meta-card"> | |||||
<h4 className="overview-meta-header"> | |||||
{translate('overview.about_this_project', qualifier)} | |||||
</h4> | |||||
{description !== undefined && <p className="overview-meta-description">{description}</p>} | |||||
{isProject && ( | |||||
<MetaTags component={component} onComponentChange={this.props.onComponentChange} /> | |||||
)} | |||||
<MetaSize branch={branch} component={component} measures={this.props.measures} /> | |||||
</div> | |||||
<AnalysesList | <AnalysesList | ||||
branch={branch} | branch={branch} | ||||
qualifier={component.qualifier} | qualifier={component.qualifier} | ||||
/> | /> | ||||
{shouldShowQualityGate && ( | |||||
<MetaQualityGate | |||||
gate={qualityGate} | |||||
organization={hasOrganization && component.organization} | |||||
/> | |||||
)} | |||||
{shouldShowQualityProfiles && ( | |||||
<MetaQualityProfiles | |||||
component={component} | |||||
customOrganizations={organizationsEnabled} | |||||
organization={component.organization} | |||||
profiles={qualityProfiles} | |||||
/> | |||||
{isProject && ( | |||||
<div className="overview-meta-card"> | |||||
{qualityGate && ( | |||||
<MetaQualityGate | |||||
organization={organizationsEnabled ? component.organization : undefined} | |||||
qualityGate={qualityGate} | |||||
/> | |||||
)} | |||||
{qualityProfiles && | |||||
qualityProfiles.length > 0 && ( | |||||
<MetaQualityProfiles | |||||
headerClassName={qualityGate ? 'big-spacer-top' : undefined} | |||||
organization={organizationsEnabled ? component.organization : undefined} | |||||
profiles={qualityProfiles} | |||||
/> | |||||
)} | |||||
</div> | |||||
)} | )} | ||||
{isProject && <MetaLinks component={component} />} | {isProject && <MetaLinks component={component} />} | ||||
<MetaKey component={component} /> | |||||
{hasOrganization && <MetaOrganizationKey component={component} />} | |||||
<div className="overview-meta-card"> | |||||
<MetaKey componentKey={component.key} qualifier={component.qualifier} /> | |||||
{organizationsEnabled && <MetaOrganizationKey organization={component.organization} />} | |||||
</div> | |||||
{onSonarCloud && | {onSonarCloud && | ||||
isProject && | isProject && |
* along with this program; if not, write to the Free Software Foundation, | * along with this program; if not, write to the Free Software Foundation, | ||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
*/ | */ | ||||
import React from 'react'; | |||||
import * as React from 'react'; | |||||
import ClipboardButton from '../../../components/controls/ClipboardButton'; | |||||
import { translate } from '../../../helpers/l10n'; | import { translate } from '../../../helpers/l10n'; | ||||
const MetaOrganizationKey = ({ component }) => { | |||||
interface Props { | |||||
componentKey: string; | |||||
qualifier: string; | |||||
} | |||||
export default function MetaKey({ componentKey, qualifier }: Props) { | |||||
return ( | return ( | ||||
<div className="overview-meta-card"> | |||||
<h4 className="overview-meta-header">{translate('organization_key')}</h4> | |||||
<input | |||||
className="overview-key" | |||||
type="text" | |||||
value={component.organization} | |||||
readOnly={true} | |||||
onClick={e => e.target.select()} | |||||
/> | |||||
</div> | |||||
<> | |||||
<h4 className="overview-meta-header">{translate('overview.project_key', qualifier)}</h4> | |||||
<div className="display-flex-center"> | |||||
<input className="overview-key" type="text" value={componentKey} readOnly={true} /> | |||||
<ClipboardButton className="little-spacer-left" copyValue={componentKey} /> | |||||
</div> | |||||
</> | |||||
); | ); | ||||
}; | |||||
export default MetaOrganizationKey; | |||||
} |
import { getProjectLinks, ProjectLink } from '../../../api/projectLinks'; | import { getProjectLinks, ProjectLink } from '../../../api/projectLinks'; | ||||
import { orderLinks } from '../../project-admin/links/utils'; | import { orderLinks } from '../../project-admin/links/utils'; | ||||
import { LightComponent } from '../../../app/types'; | import { LightComponent } from '../../../app/types'; | ||||
import { translate } from '../../../helpers/l10n'; | |||||
interface Props { | interface Props { | ||||
component: LightComponent; | component: LightComponent; | ||||
return ( | return ( | ||||
<div className="overview-meta-card"> | <div className="overview-meta-card"> | ||||
<h4 className="overview-meta-header">{translate('overview.external_links')}</h4> | |||||
<ul className="overview-meta-list"> | <ul className="overview-meta-list"> | ||||
{orderedLinks.map(link => <MetaLink key={link.id} link={link} />)} | {orderedLinks.map(link => <MetaLink key={link.id} link={link} />)} | ||||
</ul> | </ul> |
* along with this program; if not, write to the Free Software Foundation, | * along with this program; if not, write to the Free Software Foundation, | ||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
*/ | */ | ||||
import React from 'react'; | |||||
import * as React from 'react'; | |||||
import { translate } from '../../../helpers/l10n'; | import { translate } from '../../../helpers/l10n'; | ||||
import ClipboardButton from '../../../components/controls/ClipboardButton'; | |||||
const MetaKey = ({ component }) => { | |||||
interface Props { | |||||
organization: string; | |||||
} | |||||
export default function MetaOrganizationKey({ organization }: Props) { | |||||
return ( | return ( | ||||
<div className="overview-meta-card"> | |||||
<h4 className="overview-meta-header">{translate('key')}</h4> | |||||
<input | |||||
className="overview-key" | |||||
type="text" | |||||
value={component.key} | |||||
readOnly={true} | |||||
onClick={e => e.target.select()} | |||||
/> | |||||
</div> | |||||
<> | |||||
<h4 className="overview-meta-header big-spacer-top">{translate('organization_key')}</h4> | |||||
<div className="display-flex-center"> | |||||
<input className="overview-key" type="text" value={organization} readOnly={true} /> | |||||
<ClipboardButton className="little-spacer-left" copyValue={organization} /> | |||||
</div> | |||||
</> | |||||
); | ); | ||||
}; | |||||
export default MetaKey; | |||||
} |
* along with this program; if not, write to the Free Software Foundation, | * along with this program; if not, write to the Free Software Foundation, | ||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
*/ | */ | ||||
import React from 'react'; | |||||
import * as React from 'react'; | |||||
import { Link } from 'react-router'; | import { Link } from 'react-router'; | ||||
import { translate } from '../../../helpers/l10n'; | import { translate } from '../../../helpers/l10n'; | ||||
import { getQualityGateUrl } from '../../../helpers/urls'; | import { getQualityGateUrl } from '../../../helpers/urls'; | ||||
const MetaQualityGate = ({ gate, organization }) => { | |||||
interface Props { | |||||
organization?: string; | |||||
qualityGate: { isDefault?: boolean; key: string; name: string }; | |||||
} | |||||
export default function MetaQualityGate({ qualityGate, organization }: Props) { | |||||
return ( | return ( | ||||
<div className="overview-meta-card"> | |||||
<> | |||||
<h4 className="overview-meta-header">{translate('overview.quality_gate')}</h4> | <h4 className="overview-meta-header">{translate('overview.quality_gate')}</h4> | ||||
<ul className="overview-meta-list"> | <ul className="overview-meta-list"> | ||||
<li> | <li> | ||||
{gate.isDefault && ( | |||||
{qualityGate.isDefault && ( | |||||
<span className="note spacer-right">{'(' + translate('default') + ')'}</span> | <span className="note spacer-right">{'(' + translate('default') + ')'}</span> | ||||
)} | )} | ||||
<Link to={getQualityGateUrl(gate.key, organization)}>{gate.name}</Link> | |||||
<Link to={getQualityGateUrl(qualityGate.key, organization)}>{qualityGate.name}</Link> | |||||
</li> | </li> | ||||
</ul> | </ul> | ||||
</div> | |||||
</> | |||||
); | ); | ||||
}; | |||||
export default MetaQualityGate; | |||||
} |
* along with this program; if not, write to the Free Software Foundation, | * along with this program; if not, write to the Free Software Foundation, | ||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
*/ | */ | ||||
// @flow | |||||
import React from 'react'; | |||||
import * as React from 'react'; | |||||
import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||
import { Link } from 'react-router'; | import { Link } from 'react-router'; | ||||
import * as classNames from 'classnames'; | |||||
import Tooltip from '../../../components/controls/Tooltip'; | import Tooltip from '../../../components/controls/Tooltip'; | ||||
import { translate, translateWithParameters } from '../../../helpers/l10n'; | import { translate, translateWithParameters } from '../../../helpers/l10n'; | ||||
import { getQualityProfileUrl } from '../../../helpers/urls'; | import { getQualityProfileUrl } from '../../../helpers/urls'; | ||||
import { searchRules } from '../../../api/rules'; | import { searchRules } from '../../../api/rules'; | ||||
import { getLanguages } from '../../../store/rootReducer'; | import { getLanguages } from '../../../store/rootReducer'; | ||||
class MetaQualityProfiles extends React.PureComponent { | |||||
/*:: mounted: boolean; */ | |||||
interface StateProps { | |||||
languages: { [key: string]: { name: string } }; | |||||
} | |||||
/*:: props: { | |||||
component: { organization: string }, | |||||
customOrganizations: boolean, | |||||
languages: { [string]: { name: string } }, | |||||
organization: string | void; | |||||
profiles: Array<{ key: string, language: string, name: string }> | |||||
}; | |||||
*/ | |||||
interface OwnProps { | |||||
headerClassName?: string; | |||||
organization?: string; | |||||
profiles: { key: string; language: string; name: string }[]; | |||||
} | |||||
state = { | |||||
deprecatedByKey: {} | |||||
}; | |||||
interface State { | |||||
deprecatedByKey: { [key: string]: number }; | |||||
} | |||||
class MetaQualityProfiles extends React.PureComponent<StateProps & OwnProps, State> { | |||||
mounted: boolean; | |||||
state: State = { deprecatedByKey: {} }; | |||||
componentDidMount() { | componentDidMount() { | ||||
this.mounted = true; | this.mounted = true; | ||||
Promise.all(requests).then( | Promise.all(requests).then( | ||||
responses => { | responses => { | ||||
if (this.mounted) { | if (this.mounted) { | ||||
const deprecatedByKey = {}; | |||||
const deprecatedByKey: { [key: string]: number } = {}; | |||||
responses.forEach((count, i) => { | responses.forEach((count, i) => { | ||||
const profileKey = this.props.profiles[i].key; | const profileKey = this.props.profiles[i].key; | ||||
deprecatedByKey[profileKey] = count; | deprecatedByKey[profileKey] = count; | ||||
); | ); | ||||
} | } | ||||
loadDeprecatedRulesForProfile(profileKey) { | |||||
loadDeprecatedRulesForProfile(profileKey: string) { | |||||
const data = { | const data = { | ||||
activation: 'true', | activation: 'true', | ||||
organization: this.props.organization, | organization: this.props.organization, | ||||
return searchRules(data).then(r => r.total); | return searchRules(data).then(r => r.total); | ||||
} | } | ||||
getDeprecatedRulesCount(profile) { | |||||
getDeprecatedRulesCount(profile: { key: string }) { | |||||
const count = this.state.deprecatedByKey[profile.key]; | const count = this.state.deprecatedByKey[profile.key]; | ||||
return count || 0; | return count || 0; | ||||
} | } | ||||
renderProfile(profile) { | |||||
renderProfile(profile: { key: string; language: string; name: string }) { | |||||
const languageFromStore = this.props.languages[profile.language]; | const languageFromStore = this.props.languages[profile.language]; | ||||
const languageName = languageFromStore ? languageFromStore.name : profile.language; | const languageName = languageFromStore ? languageFromStore.name : profile.language; | ||||
const path = this.props.customOrganizations | |||||
? getQualityProfileUrl(profile.name, profile.language, this.props.component.organization) | |||||
: getQualityProfileUrl(profile.name, profile.language); | |||||
const path = getQualityProfileUrl(profile.name, profile.language, this.props.organization); | |||||
const inner = ( | const inner = ( | ||||
<div className="text-ellipsis"> | <div className="text-ellipsis"> | ||||
} | } | ||||
render() { | render() { | ||||
const { profiles } = this.props; | |||||
const { headerClassName, profiles } = this.props; | |||||
return ( | return ( | ||||
<div className="overview-meta-card"> | |||||
<h4 className="overview-meta-header">{translate('overview.quality_profiles')}</h4> | |||||
<> | |||||
<h4 className={classNames('overview-meta-header', headerClassName)}> | |||||
{translate('overview.quality_profiles')} | |||||
</h4> | |||||
<ul className="overview-meta-list"> | <ul className="overview-meta-list"> | ||||
{profiles.map(profile => this.renderProfile(profile))} | {profiles.map(profile => this.renderProfile(profile))} | ||||
</ul> | </ul> | ||||
</div> | |||||
</> | |||||
); | ); | ||||
} | } | ||||
} | } | ||||
const mapStateToProps = state => ({ | |||||
const mapStateToProps = (state: any) => ({ | |||||
languages: getLanguages(state) | languages: getLanguages(state) | ||||
}); | }); | ||||
export default connect(mapStateToProps)(MetaQualityProfiles); | |||||
export default connect<StateProps, {}, OwnProps>(mapStateToProps)(MetaQualityProfiles); |
} | } | ||||
return ( | return ( | ||||
<div id="overview-size" className="overview-meta-card"> | |||||
<div className="big-spacer-top" id="overview-size"> | |||||
{this.props.component.qualifier === 'APP' && this.renderProjects()} | {this.props.component.qualifier === 'APP' && this.renderProjects()} | ||||
{this.renderLoC(ncloc)} | {this.renderLoC(ncloc)} | ||||
{this.renderLoCDistribution()} | {this.renderLoCDistribution()} |
if (this.canUpdateTags()) { | if (this.canUpdateTags()) { | ||||
return ( | return ( | ||||
<div className="overview-meta-card overview-meta-tags" ref={card => (this.card = card)}> | |||||
<div className="big-spacer-top overview-meta-tags" ref={card => (this.card = card)}> | |||||
<button | <button | ||||
className="button-link" | className="button-link" | ||||
onClick={this.handleClick} | onClick={this.handleClick} | ||||
); | ); | ||||
} else { | } else { | ||||
return ( | return ( | ||||
<div className="overview-meta-card overview-meta-tags"> | |||||
<div className="big-spacer-top overview-meta-tags"> | |||||
<TagsList | <TagsList | ||||
allowUpdate={false} | allowUpdate={false} | ||||
className="note" | className="note" |
exports[`should open the tag selector on click 1`] = ` | exports[`should open the tag selector on click 1`] = ` | ||||
<div | <div | ||||
className="overview-meta-card overview-meta-tags" | |||||
className="big-spacer-top overview-meta-tags" | |||||
> | > | ||||
<button | <button | ||||
className="button-link" | className="button-link" | ||||
exports[`should open the tag selector on click 2`] = ` | exports[`should open the tag selector on click 2`] = ` | ||||
<div | <div | ||||
className="overview-meta-card overview-meta-tags" | |||||
className="big-spacer-top overview-meta-tags" | |||||
> | > | ||||
<button | <button | ||||
className="button-link" | className="button-link" | ||||
exports[`should open the tag selector on click 3`] = ` | exports[`should open the tag selector on click 3`] = ` | ||||
<div | <div | ||||
className="overview-meta-card overview-meta-tags" | |||||
className="big-spacer-top overview-meta-tags" | |||||
> | > | ||||
<button | <button | ||||
className="button-link" | className="button-link" | ||||
exports[`should render with tags and admin rights 1`] = ` | exports[`should render with tags and admin rights 1`] = ` | ||||
<div | <div | ||||
className="overview-meta-card overview-meta-tags" | |||||
className="big-spacer-top overview-meta-tags" | |||||
> | > | ||||
<button | <button | ||||
className="button-link" | className="button-link" | ||||
exports[`should render without tags and admin rights 1`] = ` | exports[`should render without tags and admin rights 1`] = ` | ||||
<div | <div | ||||
className="overview-meta-card overview-meta-tags" | |||||
className="big-spacer-top overview-meta-tags" | |||||
> | > | ||||
<TagsList | <TagsList | ||||
allowUpdate={false} | allowUpdate={false} |
transition: transform 0.5s ease, opacity 0.5s ease; | transition: transform 0.5s ease, opacity 0.5s ease; | ||||
} | } | ||||
.overview-sidebar { | |||||
margin-top: calc(-1 * var(--pagePadding)); | |||||
margin-bottom: calc(-1 * var(--pagePadding)); | |||||
margin-left: var(--pagePadding); | |||||
padding-left: calc(var(--pagePadding) - 1px); | |||||
padding-top: var(--pagePadding); | |||||
padding-bottom: var(--pagePadding); | |||||
border-left: 1px solid var(--barBorderColor); | |||||
} | |||||
/* | /* | ||||
* Title | * Title | ||||
*/ | */ | ||||
.overview-meta-card { | .overview-meta-card { | ||||
min-width: 200px; | min-width: 200px; | ||||
padding-bottom: 20px; | |||||
box-sizing: border-box; | box-sizing: border-box; | ||||
} | } | ||||
.overview-meta-card + .overview-meta-card { | |||||
margin-top: calc(2 * var(--gridSize)); | |||||
padding-top: calc(2 * var(--gridSize) - 1px); | |||||
border-top: 1px solid var(--barBorderColor); | |||||
} | |||||
.overview-meta-description { | .overview-meta-description { | ||||
margin-top: calc(-0.5 * var(--gridSize)); | |||||
line-height: 1.5; | line-height: 1.5; | ||||
color: var(--secondFontColor); | |||||
} | } | ||||
.overview-meta-header { | .overview-meta-header { | ||||
margin-bottom: calc(0.5 * var(--gridSize)); | |||||
color: var(--baseFontColor); | color: var(--baseFontColor); | ||||
} | } | ||||
} | } | ||||
.overview-analysis + .overview-analysis { | .overview-analysis + .overview-analysis { | ||||
margin-top: 8px; | |||||
padding-top: 8px; | |||||
border-top: 1px solid var(--barBorderColor); | |||||
margin-top: calc(2 * var(--gridSize)); | |||||
} | } | ||||
.overview-analysis-graph { | .overview-analysis-graph { |
overview.started_on_x=Started on {0} | overview.started_on_x=Started on {0} | ||||
overview.last_analysis_on_x=Last analysis on {0} | overview.last_analysis_on_x=Last analysis on {0} | ||||
overview.on_new_code=On New Code | overview.on_new_code=On New Code | ||||
overview.about_this_project.APP=About This Application | |||||
overview.about_this_project.TRK=About This Project | |||||
overview.about_this_project.BRC=About This Sub-Project | |||||
overview.about_this_project.DIR=About This Directory | |||||
overview.project_activity.APP=Application Activity | |||||
overview.project_activity.TRK=Project Activity | |||||
overview.project_activity.BRC=Sub-Project Activity | |||||
overview.project_activity.DIR=Directory Activity | |||||
overview.external_links=External Links | |||||
overview.project_key.APP=Application Key | |||||
overview.project_key.TRK=Project Key | |||||
overview.project_key.BRC=Sub-Project Key | |||||
overview.project_key.DIR=Directory Key | |||||
overview.metric.code_smells=Code Smells | overview.metric.code_smells=Code Smells | ||||
overview.metric.new_code_smells=New Code Smells | overview.metric.new_code_smells=New Code Smells |