Merge "Web: fix tabs on project page"
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
// Copyright 2018 Red Hat, Inc
|
||||
// Copyright 2022 Acme Gating, LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
@@ -12,68 +13,44 @@
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
import * as React from 'react'
|
||||
import React, { useState } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {
|
||||
Nav,
|
||||
NavItem,
|
||||
TabContainer,
|
||||
TabPane,
|
||||
TabContent,
|
||||
} from 'patternfly-react'
|
||||
Tabs,
|
||||
Tab,
|
||||
} from '@patternfly/react-core'
|
||||
|
||||
import ProjectVariant from './ProjectVariant'
|
||||
|
||||
|
||||
class Project extends React.Component {
|
||||
static propTypes = {
|
||||
project: PropTypes.object.isRequired,
|
||||
}
|
||||
function Project(props) {
|
||||
const [variantIdx, setVariantIdx] = useState(0)
|
||||
const { project } = props
|
||||
|
||||
state = {
|
||||
variantIdx: 0,
|
||||
}
|
||||
|
||||
renderVariantTitle (variant, selected) {
|
||||
let title = variant.default_branch
|
||||
if (selected) {
|
||||
title = <strong>{title}</strong>
|
||||
}
|
||||
function renderVariantTitle (variant) {
|
||||
let title = variant.source_context.project === project.name ?
|
||||
variant.source_context.branch : variant.source_context.project
|
||||
return title
|
||||
}
|
||||
|
||||
render () {
|
||||
const { project } = this.props
|
||||
const { variantIdx } = this.state
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Tabs activeKey={variantIdx}
|
||||
onSelect={(event, tabIndex) => setVariantIdx(tabIndex)}
|
||||
isBox>
|
||||
{project.configs.map((variant, idx) => (
|
||||
<Tab key={idx} eventKey={idx}
|
||||
title={renderVariantTitle(variant)}>
|
||||
<ProjectVariant variant={variant} />
|
||||
</Tab>
|
||||
))}
|
||||
</Tabs>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<h2>{project.canonical_name}</h2>
|
||||
<TabContainer id="zuul-project">
|
||||
<div>
|
||||
<Nav bsClass="nav nav-tabs nav-tabs-pf">
|
||||
{project.configs.map((variant, idx) => (
|
||||
<NavItem
|
||||
key={idx}
|
||||
onClick={() => this.setState({variantIdx: idx})}>
|
||||
<div>
|
||||
{this.renderVariantTitle(variant, variantIdx === idx)}
|
||||
</div>
|
||||
</NavItem>
|
||||
))}
|
||||
</Nav>
|
||||
<TabContent>
|
||||
<TabPane>
|
||||
{project.configs[variantIdx] && (
|
||||
<ProjectVariant variant={project.configs[variantIdx]} />
|
||||
)}
|
||||
</TabPane>
|
||||
</TabContent>
|
||||
</div>
|
||||
</TabContainer>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
Project.propTypes = {
|
||||
project: PropTypes.object.isRequired,
|
||||
}
|
||||
|
||||
export default Project
|
||||
|
||||
@@ -18,64 +18,68 @@ import { connect } from 'react-redux'
|
||||
import { Link } from 'react-router-dom'
|
||||
|
||||
|
||||
class ProjectVariant extends React.Component {
|
||||
static propTypes = {
|
||||
tenant: PropTypes.object,
|
||||
variant: PropTypes.object.isRequired
|
||||
function ProjectVariant(props) {
|
||||
const { tenant, variant } = props
|
||||
const rows = []
|
||||
|
||||
rows.push({label: 'Merge mode', value: variant.merge_mode})
|
||||
|
||||
if (variant.templates.length > 0) {
|
||||
const templateList = (
|
||||
<ul className='list-group'>
|
||||
{variant.templates.map((item, idx) => (
|
||||
<li className='list-group-item' key={idx}>{item}</li>))}
|
||||
</ul>
|
||||
)
|
||||
rows.push({label: 'Templates', value: templateList})
|
||||
}
|
||||
|
||||
render () {
|
||||
const { tenant, variant } = this.props
|
||||
const rows = []
|
||||
|
||||
rows.push({label: 'Merge mode', value: variant.merge_mode})
|
||||
|
||||
if (variant.templates.length > 0) {
|
||||
const templateList = (
|
||||
variant.pipelines.forEach(pipeline => {
|
||||
// TODO: either adds job link anchor to load the right variant
|
||||
// and/or show the job variant config in a modal?
|
||||
const jobList = (
|
||||
<React.Fragment>
|
||||
{pipeline.queue_name && (
|
||||
<p><strong>Queue: </strong> {pipeline.queue_name} </p>)}
|
||||
<ul className='list-group'>
|
||||
{variant.templates.map((item, idx) => (
|
||||
<li className='list-group-item' key={idx}>{item}</li>))}
|
||||
{pipeline.jobs.map((item, idx) => (
|
||||
<li className='list-group-item' key={idx}>
|
||||
<Link to={tenant.linkPrefix + '/job/' + item[0].name}>
|
||||
{item[0].name}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)
|
||||
rows.push({label: 'Templates', value: templateList})
|
||||
}
|
||||
|
||||
variant.pipelines.forEach(pipeline => {
|
||||
// TODO: either adds job link anchor to load the right variant
|
||||
// and/or show the job variant config in a modal?
|
||||
const jobList = (
|
||||
<React.Fragment>
|
||||
{pipeline.queue_name && (
|
||||
<p><strong>Queue: </strong> {pipeline.queue_name} </p>)}
|
||||
<ul className='list-group'>
|
||||
{pipeline.jobs.map((item, idx) => (
|
||||
<li className='list-group-item' key={idx}>
|
||||
<Link to={tenant.linkPrefix + '/job/' + item[0].name}>
|
||||
{item[0].name}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</React.Fragment>
|
||||
)
|
||||
rows.push({label: pipeline.name + ' jobs', value: jobList})
|
||||
})
|
||||
|
||||
return (
|
||||
<div>
|
||||
<table className='table table-striped table-bordered'>
|
||||
<tbody>
|
||||
{rows.map(item => (
|
||||
<tr key={item.label}>
|
||||
<td style={{width: '10%'}}>{item.label}</td>
|
||||
<td>{item.value}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
)
|
||||
rows.push({label: pipeline.name + ' jobs', value: jobList})
|
||||
})
|
||||
|
||||
return (
|
||||
<div>
|
||||
<table className='table table-striped table-bordered'>
|
||||
<tbody>
|
||||
{rows.map(item => (
|
||||
<tr key={item.label}>
|
||||
<td style={{width: '10%'}}>{item.label}</td>
|
||||
<td>{item.value}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
ProjectVariant.propTypes = {
|
||||
tenant: PropTypes.object,
|
||||
variant: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
tenant: state.tenant,
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(state => ({tenant: state.tenant}))(ProjectVariant)
|
||||
export default connect(mapStateToProps)(ProjectVariant)
|
||||
|
||||
@@ -3,6 +3,12 @@ body {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Make the H2 header inline-block so that the refresh icon/button can
|
||||
share space with it floating on the right. */
|
||||
h2 {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.pf-c-title {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Copyright 2018 Red Hat, Inc
|
||||
// Copyright 2022 Acme Gating, LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
@@ -12,67 +13,74 @@
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
import * as React from 'react'
|
||||
import React, { useEffect, useCallback } from 'react'
|
||||
import { connect } from 'react-redux'
|
||||
import PropTypes from 'prop-types'
|
||||
import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
||||
|
||||
import {
|
||||
PageSection,
|
||||
PageSectionVariants,
|
||||
Text,
|
||||
TextContent,
|
||||
} from '@patternfly/react-core'
|
||||
import Project from '../containers/project/Project'
|
||||
import JobGraph from '../containers/jobgraph/JobGraph'
|
||||
import { fetchProjectIfNeeded } from '../actions/project'
|
||||
import { Fetchable } from '../containers/Fetching'
|
||||
|
||||
|
||||
class ProjectPage extends React.Component {
|
||||
static propTypes = {
|
||||
match: PropTypes.object.isRequired,
|
||||
tenant: PropTypes.object,
|
||||
remoteData: PropTypes.object,
|
||||
dispatch: PropTypes.func
|
||||
}
|
||||
function ProjectPage(props) {
|
||||
const { tenant, fetchProjectIfNeeded, remoteData } = props
|
||||
const { projectName } = props.match.params
|
||||
const tenantProjects = remoteData.projects[tenant.name]
|
||||
|
||||
updateData = (force) => {
|
||||
this.props.dispatch(fetchProjectIfNeeded(
|
||||
this.props.tenant, this.props.match.params.projectName, force))
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
document.title = 'Zuul Project | ' + this.props.match.params.projectName
|
||||
if (this.props.tenant.name) {
|
||||
this.updateData()
|
||||
const updateData = useCallback((force) => {
|
||||
if (tenant.name) {
|
||||
fetchProjectIfNeeded(tenant, projectName, force)
|
||||
}
|
||||
}
|
||||
}, [tenant, projectName, fetchProjectIfNeeded])
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
if (this.props.tenant.name !== prevProps.tenant.name) {
|
||||
this.updateData()
|
||||
}
|
||||
}
|
||||
useEffect(() => {
|
||||
document.title = 'Zuul Project | ' + projectName
|
||||
updateData()
|
||||
}, [tenant, projectName, updateData])
|
||||
|
||||
render () {
|
||||
const { remoteData } = this.props
|
||||
const tenantProjects = remoteData.projects[this.props.tenant.name]
|
||||
const projectName = this.props.match.params.projectName
|
||||
return (
|
||||
return (
|
||||
<>
|
||||
<PageSection variant={PageSectionVariants.light}>
|
||||
<PageSection style={{paddingRight: '5px'}}>
|
||||
<Fetchable
|
||||
isFetching={remoteData.isFetching}
|
||||
fetchCallback={this.updateData}
|
||||
/>
|
||||
</PageSection>
|
||||
<TextContent>
|
||||
<Text component="h2">Project {projectName}</Text>
|
||||
<Fetchable
|
||||
isFetching={remoteData.isFetching}
|
||||
fetchCallback={updateData}
|
||||
/>
|
||||
</TextContent>
|
||||
{tenantProjects && tenantProjects[projectName] &&
|
||||
<>
|
||||
<Project project={tenantProjects[projectName]} />
|
||||
<JobGraph project={tenantProjects[projectName]} />
|
||||
</>
|
||||
}
|
||||
</PageSection>
|
||||
)
|
||||
</PageSection>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
ProjectPage.propTypes = {
|
||||
match: PropTypes.object.isRequired,
|
||||
tenant: PropTypes.object,
|
||||
remoteData: PropTypes.object,
|
||||
fetchProjectIfNeeded: PropTypes.func,
|
||||
}
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
tenant: state.tenant,
|
||||
remoteData: state.project,
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(state => ({
|
||||
tenant: state.tenant,
|
||||
remoteData: state.project,
|
||||
}))(ProjectPage)
|
||||
const mapDispatchToProps = {
|
||||
fetchProjectIfNeeded,
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ProjectPage)
|
||||
|
||||
Reference in New Issue
Block a user