PF4: Update "fetching info ..." and refresh animation
Currently the "refresh" animations look quite different depending on the page and the type of event (refresh button, initial page load, ...). This tries to make a start by updating the "Fetching info ..." animation (shown on initial page load) with Patternfly's "empty state" pattern [1] in combination with an animated spinner. For "Refreshable" pages, a similar animation is used in the upper right corner (like before) but with an updated spinner and icon. By using a dedicated React component rather than a base class, the "refresh" button can be more nicely integrated into the layout of the page/container and it doesn't look "out of scope" for the refreshable component. For the API page I've removed the refresh completely since it wasn't implemented at all. [1] https://www.patternfly.org/v4/documentation/react/components/emptystate Change-Id: I2274c212f14aece27ff49bfc7900d9b1a0fd01d0
This commit is contained in:
parent
141347e082
commit
ed9d0446d5
|
@ -64,6 +64,7 @@ import {
|
|||
} from '@patternfly/react-icons'
|
||||
|
||||
import ErrorBoundary from './containers/ErrorBoundary'
|
||||
import { Fetching } from './containers/Fetching'
|
||||
import SelectTz from './containers/timezone/SelectTz'
|
||||
import logo from './images/logo.svg'
|
||||
import { clearError } from './actions/errors'
|
||||
|
@ -126,7 +127,7 @@ class App extends React.Component {
|
|||
const allRoutes = []
|
||||
|
||||
if (info.isFetching) {
|
||||
return (<h2>Fetching info...</h2>)
|
||||
return <Fetching />
|
||||
}
|
||||
this.menu
|
||||
// Do not include '/tenants' route in white-label setup
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
// Copyright 2020 BMW Group
|
||||
//
|
||||
// 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
|
||||
// a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
import * as React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import {
|
||||
Button,
|
||||
Title,
|
||||
EmptyState,
|
||||
EmptyStateVariant,
|
||||
Spinner,
|
||||
} from '@patternfly/react-core'
|
||||
|
||||
import { SyncIcon } from '@patternfly/react-icons'
|
||||
|
||||
function Fetchable(props) {
|
||||
const { isFetching, fetchCallback } = props
|
||||
let content = null
|
||||
|
||||
if (isFetching) {
|
||||
content = (
|
||||
<div>
|
||||
<Spinner size="md" />
|
||||
</div>
|
||||
)
|
||||
} else {
|
||||
content = (
|
||||
<Button
|
||||
variant="link"
|
||||
isInline icon={<SyncIcon />}
|
||||
onClick={() => {fetchCallback({force: true})}}
|
||||
style={{textDecoration: 'none'}}
|
||||
>
|
||||
refresh
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{float: 'right'}}>
|
||||
{content}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Fetchable.propTypes = {
|
||||
isFetching: PropTypes.bool,
|
||||
fetchCallback: PropTypes.func,
|
||||
}
|
||||
|
||||
function Fetching() {
|
||||
return (
|
||||
<EmptyState variant={EmptyStateVariant.small}>
|
||||
<Spinner />
|
||||
<Title headingLevel="h4" size="lg">
|
||||
Fetching info...
|
||||
</Title>
|
||||
</EmptyState>
|
||||
)
|
||||
}
|
||||
|
||||
export { Fetchable, Fetching }
|
|
@ -1,57 +0,0 @@
|
|||
// Copyright 2018 Red Hat, Inc
|
||||
//
|
||||
// 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
|
||||
// a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
// Boiler plate code to manage refresh button
|
||||
|
||||
import * as React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {
|
||||
Icon,
|
||||
Spinner
|
||||
} from 'patternfly-react'
|
||||
|
||||
|
||||
class Refreshable extends React.Component {
|
||||
static propTypes = {
|
||||
tenant: PropTypes.object,
|
||||
remoteData: PropTypes.object,
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
if (this.props.tenant.name) {
|
||||
this.updateData()
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
if (this.props.tenant.name !== prevProps.tenant.name) {
|
||||
this.updateData()
|
||||
}
|
||||
}
|
||||
|
||||
renderSpinner () {
|
||||
const { remoteData } = this.props
|
||||
return (
|
||||
<Spinner loading={ remoteData.isFetching }>
|
||||
{/* Lint warning jsx-a11y/anchor-is-valid */}
|
||||
{/* eslint-disable-next-line */}
|
||||
<a className="refresh" onClick={() => {this.updateData(true)}}>
|
||||
<Icon type="fa" name="refresh" /> refresh
|
||||
</a>
|
||||
</Spinner>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Refreshable
|
|
@ -18,16 +18,17 @@ import PropTypes from 'prop-types'
|
|||
import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
||||
|
||||
import { fetchBuildIfNeeded } from '../actions/build'
|
||||
import Refreshable from '../containers/Refreshable'
|
||||
import { Fetchable } from '../containers/Fetching'
|
||||
import Build from '../containers/build/Build'
|
||||
import Summary from '../containers/build/Summary'
|
||||
|
||||
|
||||
class BuildPage extends Refreshable {
|
||||
class BuildPage extends React.Component {
|
||||
static propTypes = {
|
||||
match: PropTypes.object.isRequired,
|
||||
remoteData: PropTypes.object,
|
||||
tenant: PropTypes.object
|
||||
tenant: PropTypes.object,
|
||||
dispatch: PropTypes.func,
|
||||
}
|
||||
|
||||
updateData = (force) => {
|
||||
|
@ -37,7 +38,15 @@ class BuildPage extends Refreshable {
|
|||
|
||||
componentDidMount () {
|
||||
document.title = 'Zuul Build'
|
||||
super.componentDidMount()
|
||||
if (this.props.tenant.name) {
|
||||
this.updateData()
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
if (this.props.tenant.name !== prevProps.tenant.name) {
|
||||
this.updateData()
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
|
@ -45,9 +54,12 @@ class BuildPage extends Refreshable {
|
|||
const build = remoteData.builds[this.props.match.params.buildId]
|
||||
return (
|
||||
<PageSection variant={PageSectionVariants.light}>
|
||||
<div style={{float: 'right'}}>
|
||||
{this.renderSpinner()}
|
||||
</div>
|
||||
<PageSection style={{paddingRight: '5px'}}>
|
||||
<Fetchable
|
||||
isFetching={remoteData.isFetching}
|
||||
fetchCallback={this.updateData}
|
||||
/>
|
||||
</PageSection>
|
||||
{build &&
|
||||
<Build build={build} active='summary'>
|
||||
<Summary build={build}/>
|
||||
|
|
|
@ -18,16 +18,18 @@ import PropTypes from 'prop-types'
|
|||
import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
||||
|
||||
import { fetchBuildIfNeeded } from '../actions/build'
|
||||
import Refreshable from '../containers/Refreshable'
|
||||
import { Fetchable } from '../containers/Fetching'
|
||||
import Build from '../containers/build/Build'
|
||||
import Console from '../containers/build/Console'
|
||||
|
||||
|
||||
class BuildConsolePage extends Refreshable {
|
||||
class BuildConsolePage extends React.Component {
|
||||
static propTypes = {
|
||||
match: PropTypes.object.isRequired,
|
||||
remoteData: PropTypes.object,
|
||||
tenant: PropTypes.object
|
||||
tenant: PropTypes.object,
|
||||
dispatch: PropTypes.func,
|
||||
location: PropTypes.object,
|
||||
}
|
||||
|
||||
updateData = (force) => {
|
||||
|
@ -37,7 +39,15 @@ class BuildConsolePage extends Refreshable {
|
|||
|
||||
componentDidMount () {
|
||||
document.title = 'Zuul Build'
|
||||
super.componentDidMount()
|
||||
if (this.props.tenant.name) {
|
||||
this.updateData()
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
if (this.props.tenant.name !== prevProps.tenant.name) {
|
||||
this.updateData()
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
|
@ -47,9 +57,12 @@ class BuildConsolePage extends Refreshable {
|
|||
|
||||
return (
|
||||
<PageSection variant={PageSectionVariants.light}>
|
||||
<div style={{float: 'right'}}>
|
||||
{this.renderSpinner()}
|
||||
</div>
|
||||
<PageSection style={{paddingRight: '5px'}}>
|
||||
<Fetchable
|
||||
isFetching={remoteData.isFetching}
|
||||
fetchCallback={this.updateData}
|
||||
/>
|
||||
</PageSection>
|
||||
{build && build.output &&
|
||||
<Build build={build} active='console'>
|
||||
<Console
|
||||
|
|
|
@ -18,16 +18,17 @@ import PropTypes from 'prop-types'
|
|||
import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
||||
|
||||
import { fetchBuildIfNeeded } from '../actions/build'
|
||||
import Refreshable from '../containers/Refreshable'
|
||||
import { Fetchable } from '../containers/Fetching'
|
||||
import Build from '../containers/build/Build'
|
||||
import Manifest from '../containers/build/Manifest'
|
||||
|
||||
|
||||
class BuildLogsPage extends Refreshable {
|
||||
class BuildLogsPage extends React.Component {
|
||||
static propTypes = {
|
||||
match: PropTypes.object.isRequired,
|
||||
remoteData: PropTypes.object,
|
||||
tenant: PropTypes.object
|
||||
tenant: PropTypes.object,
|
||||
dispatch: PropTypes.func,
|
||||
}
|
||||
|
||||
updateData = (force) => {
|
||||
|
@ -37,7 +38,15 @@ class BuildLogsPage extends Refreshable {
|
|||
|
||||
componentDidMount () {
|
||||
document.title = 'Zuul Build'
|
||||
super.componentDidMount()
|
||||
if (this.props.tenant.name) {
|
||||
this.updateData()
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
if (this.props.tenant.name !== prevProps.tenant.name) {
|
||||
this.updateData()
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
|
@ -45,9 +54,12 @@ class BuildLogsPage extends Refreshable {
|
|||
const build = remoteData.builds[this.props.match.params.buildId]
|
||||
return (
|
||||
<PageSection variant={PageSectionVariants.light}>
|
||||
<div style={{float: 'right'}}>
|
||||
{this.renderSpinner()}
|
||||
</div>
|
||||
<PageSection style={{paddingRight: '5px'}}>
|
||||
<Fetchable
|
||||
isFetching={remoteData.isFetching}
|
||||
fetchCallback={this.updateData}
|
||||
/>
|
||||
</PageSection>
|
||||
{build && build.manifest &&
|
||||
<Build build={build} active='logs'>
|
||||
<Manifest tenant={this.props.tenant} build={build}/>
|
||||
|
|
|
@ -18,15 +18,16 @@ import PropTypes from 'prop-types'
|
|||
import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
||||
|
||||
import { fetchBuildsetIfNeeded } from '../actions/build'
|
||||
import Refreshable from '../containers/Refreshable'
|
||||
import { Fetchable } from '../containers/Fetching'
|
||||
import Buildset from '../containers/build/Buildset'
|
||||
|
||||
|
||||
class BuildsetPage extends Refreshable {
|
||||
class BuildsetPage extends React.Component {
|
||||
static propTypes = {
|
||||
match: PropTypes.object.isRequired,
|
||||
remoteData: PropTypes.object,
|
||||
tenant: PropTypes.object
|
||||
tenant: PropTypes.object,
|
||||
dispatch: PropTypes.func,
|
||||
}
|
||||
|
||||
updateData = (force) => {
|
||||
|
@ -36,17 +37,29 @@ class BuildsetPage extends Refreshable {
|
|||
|
||||
componentDidMount () {
|
||||
document.title = 'Zuul Buildset'
|
||||
super.componentDidMount()
|
||||
if (this.props.tenant.name) {
|
||||
this.updateData()
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
if (this.props.tenant.name !== prevProps.tenant.name) {
|
||||
this.updateData()
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const { remoteData } = this.props
|
||||
|
||||
const buildset = remoteData.buildsets[this.props.match.params.buildsetId]
|
||||
return (
|
||||
<PageSection variant={PageSectionVariants.light}>
|
||||
<div style={{float: 'right'}}>
|
||||
{this.renderSpinner()}
|
||||
</div>
|
||||
<PageSection style={{paddingRight: '5px'}}>
|
||||
<Fetchable
|
||||
isFetching={remoteData.isFetching}
|
||||
fetchCallback={this.updateData}
|
||||
/>
|
||||
</PageSection>
|
||||
{buildset && <Buildset buildset={buildset}/>}
|
||||
</PageSection>
|
||||
)
|
||||
|
|
|
@ -19,10 +19,10 @@ import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
|||
|
||||
import { fetchChangeIfNeeded } from '../actions/change'
|
||||
import ChangePanel from '../containers/status/ChangePanel'
|
||||
import Refreshable from '../containers/Refreshable'
|
||||
import { Fetchable } from '../containers/Fetching'
|
||||
|
||||
|
||||
class ChangeStatusPage extends Refreshable {
|
||||
class ChangeStatusPage extends React.Component {
|
||||
static propTypes = {
|
||||
match: PropTypes.object.isRequired,
|
||||
tenant: PropTypes.object,
|
||||
|
@ -43,7 +43,15 @@ class ChangeStatusPage extends Refreshable {
|
|||
|
||||
componentDidMount () {
|
||||
document.title = this.props.match.params.changeId + ' | Zuul Status'
|
||||
super.componentDidMount()
|
||||
if (this.props.tenant.name) {
|
||||
this.updateData()
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
if (this.props.tenant.name !== prevProps.tenant.name) {
|
||||
this.updateData()
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
|
@ -58,9 +66,12 @@ class ChangeStatusPage extends Refreshable {
|
|||
const change = remoteData.change
|
||||
return (
|
||||
<PageSection variant={PageSectionVariants.light}>
|
||||
<div style={{float: 'right'}}>
|
||||
{this.renderSpinner()}
|
||||
</div><br />
|
||||
<PageSection style={{paddingRight: '5px'}}>
|
||||
<Fetchable
|
||||
isFetching={remoteData.isFetching}
|
||||
fetchCallback={this.updateData}
|
||||
/>
|
||||
</PageSection>
|
||||
{change && change.map((item, idx) => (
|
||||
<div className='row zuul-change-content' key={idx}>
|
||||
<ChangePanel
|
||||
|
|
|
@ -18,11 +18,11 @@ import PropTypes from 'prop-types'
|
|||
import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
||||
|
||||
import Job from '../containers/job/Job'
|
||||
import Refreshable from '../containers/Refreshable'
|
||||
import { Fetchable } from '../containers/Fetching'
|
||||
import { fetchJobIfNeeded } from '../actions/job'
|
||||
|
||||
|
||||
class JobPage extends Refreshable {
|
||||
class JobPage extends React.Component {
|
||||
static propTypes = {
|
||||
match: PropTypes.object.isRequired,
|
||||
tenant: PropTypes.object,
|
||||
|
@ -37,7 +37,9 @@ class JobPage extends Refreshable {
|
|||
|
||||
componentDidMount () {
|
||||
document.title = 'Zuul Job | ' + this.props.match.params.jobName
|
||||
super.componentDidMount()
|
||||
if (this.props.tenant.name) {
|
||||
this.updateData()
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
|
@ -53,9 +55,12 @@ class JobPage extends Refreshable {
|
|||
const jobName = this.props.match.params.jobName
|
||||
return (
|
||||
<PageSection variant={PageSectionVariants.light}>
|
||||
<div style={{float: 'right'}}>
|
||||
{this.renderSpinner()}
|
||||
</div>
|
||||
<PageSection style={{paddingRight: '5px'}}>
|
||||
<Fetchable
|
||||
isFetching={remoteData.isFetching}
|
||||
fetchCallback={this.updateData}
|
||||
/>
|
||||
</PageSection>
|
||||
{tenantJobs && tenantJobs[jobName] && <Job job={tenantJobs[jobName]} />}
|
||||
</PageSection>
|
||||
)
|
||||
|
|
|
@ -18,34 +18,46 @@ import { connect } from 'react-redux'
|
|||
import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
||||
|
||||
import { fetchJobsIfNeeded } from '../actions/jobs'
|
||||
import Refreshable from '../containers/Refreshable'
|
||||
import { Fetchable } from '../containers/Fetching'
|
||||
import Jobs from '../containers/jobs/Jobs'
|
||||
|
||||
|
||||
class JobsPage extends Refreshable {
|
||||
class JobsPage extends React.Component {
|
||||
static propTypes = {
|
||||
tenant: PropTypes.object,
|
||||
remoteData: PropTypes.object,
|
||||
dispatch: PropTypes.func
|
||||
}
|
||||
|
||||
updateData (force) {
|
||||
updateData = (force) => {
|
||||
this.props.dispatch(fetchJobsIfNeeded(this.props.tenant, force))
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
document.title = 'Zuul Jobs'
|
||||
super.componentDidMount()
|
||||
if (this.props.tenant.name) {
|
||||
this.updateData()
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
if (this.props.tenant.name !== prevProps.tenant.name) {
|
||||
this.updateData()
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const { remoteData } = this.props
|
||||
|
||||
const jobs = remoteData.jobs[this.props.tenant.name]
|
||||
return (
|
||||
<PageSection variant={PageSectionVariants.light}>
|
||||
<div style={{float: 'right'}}>
|
||||
{this.renderSpinner()}
|
||||
</div>
|
||||
<PageSection style={{paddingRight: '5px'}}>
|
||||
<Fetchable
|
||||
isFetching={remoteData.isFetching}
|
||||
fetchCallback={this.updateData}
|
||||
/>
|
||||
</PageSection>
|
||||
{jobs && jobs.length > 0 &&
|
||||
<Jobs
|
||||
jobs={jobs}
|
||||
|
|
|
@ -19,10 +19,10 @@ import { Table } from 'patternfly-react'
|
|||
import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
||||
|
||||
import { fetchLabelsIfNeeded } from '../actions/labels'
|
||||
import Refreshable from '../containers/Refreshable'
|
||||
import { Fetchable, Fetching } from '../containers/Fetching'
|
||||
|
||||
|
||||
class LabelsPage extends Refreshable {
|
||||
class LabelsPage extends React.Component {
|
||||
static propTypes = {
|
||||
tenant: PropTypes.object,
|
||||
remoteData: PropTypes.object,
|
||||
|
@ -35,7 +35,15 @@ class LabelsPage extends Refreshable {
|
|||
|
||||
componentDidMount () {
|
||||
document.title = 'Zuul Labels'
|
||||
super.componentDidMount()
|
||||
if (this.props.tenant.name) {
|
||||
this.updateData()
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
if (this.props.tenant.name !== prevProps.tenant.name) {
|
||||
this.updateData()
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
|
@ -43,7 +51,7 @@ class LabelsPage extends Refreshable {
|
|||
const labels = remoteData.labels[this.props.tenant.name]
|
||||
|
||||
if (!labels) {
|
||||
return (<p>Loading...</p>)
|
||||
return <Fetching />
|
||||
}
|
||||
|
||||
const headerFormat = value => <Table.Heading>{value}</Table.Heading>
|
||||
|
@ -61,9 +69,12 @@ class LabelsPage extends Refreshable {
|
|||
})
|
||||
return (
|
||||
<PageSection variant={PageSectionVariants.light}>
|
||||
<div style={{float: 'right'}}>
|
||||
{this.renderSpinner()}
|
||||
</div>
|
||||
<PageSection style={{paddingRight: '5px'}}>
|
||||
<Fetchable
|
||||
isFetching={remoteData.isFetching}
|
||||
fetchCallback={this.updateData}
|
||||
/>
|
||||
</PageSection>
|
||||
<Table.PfProvider
|
||||
striped
|
||||
bordered
|
||||
|
|
|
@ -19,15 +19,18 @@ import { parse } from 'query-string'
|
|||
import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
||||
|
||||
import { fetchLogfileIfNeeded } from '../actions/logfile'
|
||||
import Refreshable from '../containers/Refreshable'
|
||||
import { Fetching } from '../containers/Fetching'
|
||||
import LogFile from '../containers/logfile/LogFile'
|
||||
|
||||
|
||||
class LogFilePage extends Refreshable {
|
||||
class LogFilePage extends React.Component {
|
||||
static propTypes = {
|
||||
match: PropTypes.object.isRequired,
|
||||
remoteData: PropTypes.object,
|
||||
tenant: PropTypes.object,
|
||||
dispatch: PropTypes.func,
|
||||
location: PropTypes.object,
|
||||
build: PropTypes.object,
|
||||
}
|
||||
|
||||
state = {
|
||||
|
@ -45,7 +48,9 @@ class LogFilePage extends Refreshable {
|
|||
|
||||
componentDidMount () {
|
||||
document.title = 'Zuul Build Logfile'
|
||||
super.componentDidMount()
|
||||
if (this.props.tenant.name) {
|
||||
this.updateData()
|
||||
}
|
||||
}
|
||||
|
||||
highlightDidUpdate = (lines) => {
|
||||
|
@ -108,13 +113,14 @@ class LogFilePage extends Refreshable {
|
|||
|
||||
render () {
|
||||
const { remoteData } = this.props
|
||||
if (remoteData.isFetching) {
|
||||
return <Fetching />
|
||||
}
|
||||
|
||||
const build = this.props.build.builds[this.props.match.params.buildId]
|
||||
const severity = parse(this.props.location.search).severity
|
||||
return (
|
||||
<PageSection variant={PageSectionVariants.light}>
|
||||
<div style={{float: 'right'}}>
|
||||
{this.renderSpinner()}
|
||||
</div>
|
||||
{remoteData.data && <LogFile build={build} data={remoteData.data} severity={severity}/>}
|
||||
</PageSection>
|
||||
)
|
||||
|
|
|
@ -20,23 +20,31 @@ import * as moment from 'moment'
|
|||
import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
||||
|
||||
import { fetchNodesIfNeeded } from '../actions/nodes'
|
||||
import Refreshable from '../containers/Refreshable'
|
||||
import { Fetchable } from '../containers/Fetching'
|
||||
|
||||
|
||||
class NodesPage extends Refreshable {
|
||||
class NodesPage extends React.Component {
|
||||
static propTypes = {
|
||||
tenant: PropTypes.object,
|
||||
remoteData: PropTypes.object,
|
||||
dispatch: PropTypes.func
|
||||
}
|
||||
|
||||
updateData (force) {
|
||||
updateData = (force) => {
|
||||
this.props.dispatch(fetchNodesIfNeeded(this.props.tenant, force))
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
document.title = 'Zuul Nodes'
|
||||
super.componentDidMount()
|
||||
if (this.props.tenant.name) {
|
||||
this.updateData()
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
if (this.props.tenant.name !== prevProps.tenant.name) {
|
||||
this.updateData()
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
|
@ -83,9 +91,12 @@ class NodesPage extends Refreshable {
|
|||
})
|
||||
return (
|
||||
<PageSection variant={PageSectionVariants.light}>
|
||||
<div style={{float: 'right'}}>
|
||||
{this.renderSpinner()}
|
||||
</div>
|
||||
<PageSection style={{paddingRight: '5px'}}>
|
||||
<Fetchable
|
||||
isFetching={remoteData.isFetching}
|
||||
fetchCallback={this.updateData}
|
||||
/>
|
||||
</PageSection>
|
||||
<Table.PfProvider
|
||||
striped
|
||||
bordered
|
||||
|
|
|
@ -20,10 +20,9 @@ import 'swagger-ui/dist/swagger-ui.css'
|
|||
import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
||||
|
||||
import { fetchOpenApiIfNeeded } from '../actions/openapi'
|
||||
import Refreshable from '../containers/Refreshable'
|
||||
|
||||
|
||||
class OpenApiPage extends Refreshable {
|
||||
class OpenApiPage extends React.Component {
|
||||
static propTypes = {
|
||||
tenant: PropTypes.object,
|
||||
remoteData: PropTypes.object,
|
||||
|
@ -53,9 +52,6 @@ class OpenApiPage extends Refreshable {
|
|||
render() {
|
||||
return (
|
||||
<PageSection variant={PageSectionVariants.light}>
|
||||
<div className="pull-right" style={{display: 'flex'}}>
|
||||
{this.renderSpinner()}
|
||||
</div>
|
||||
<div id="swaggerContainer" />
|
||||
</PageSection>
|
||||
)
|
||||
|
|
|
@ -19,10 +19,10 @@ import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
|||
|
||||
import Project from '../containers/project/Project'
|
||||
import { fetchProjectIfNeeded } from '../actions/project'
|
||||
import Refreshable from '../containers/Refreshable'
|
||||
import { Fetchable } from '../containers/Fetching'
|
||||
|
||||
|
||||
class ProjectPage extends Refreshable {
|
||||
class ProjectPage extends React.Component {
|
||||
static propTypes = {
|
||||
match: PropTypes.object.isRequired,
|
||||
tenant: PropTypes.object,
|
||||
|
@ -37,7 +37,15 @@ class ProjectPage extends Refreshable {
|
|||
|
||||
componentDidMount () {
|
||||
document.title = 'Zuul Project | ' + this.props.match.params.projectName
|
||||
super.componentDidMount()
|
||||
if (this.props.tenant.name) {
|
||||
this.updateData()
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
if (this.props.tenant.name !== prevProps.tenant.name) {
|
||||
this.updateData()
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
|
@ -46,9 +54,12 @@ class ProjectPage extends Refreshable {
|
|||
const projectName = this.props.match.params.projectName
|
||||
return (
|
||||
<PageSection variant={PageSectionVariants.light}>
|
||||
<div style={{float: 'right'}}>
|
||||
{this.renderSpinner()}
|
||||
</div>
|
||||
<PageSection style={{paddingRight: '5px'}}>
|
||||
<Fetchable
|
||||
isFetching={remoteData.isFetching}
|
||||
fetchCallback={this.updateData}
|
||||
/>
|
||||
</PageSection>
|
||||
{tenantProjects && tenantProjects[projectName] &&
|
||||
<Project project={tenantProjects[projectName]} />}
|
||||
</PageSection>
|
||||
|
|
|
@ -20,31 +20,43 @@ import { Table } from 'patternfly-react'
|
|||
import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
||||
|
||||
import { fetchProjectsIfNeeded } from '../actions/projects'
|
||||
import Refreshable from '../containers/Refreshable'
|
||||
import { Fetchable, Fetching } from '../containers/Fetching'
|
||||
|
||||
|
||||
class ProjectsPage extends Refreshable {
|
||||
class ProjectsPage extends React.Component {
|
||||
static propTypes = {
|
||||
tenant: PropTypes.object,
|
||||
remoteData: PropTypes.object,
|
||||
dispatch: PropTypes.func
|
||||
}
|
||||
|
||||
updateData (force) {
|
||||
updateData = (force) => {
|
||||
this.props.dispatch(fetchProjectsIfNeeded(this.props.tenant, force))
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
document.title = 'Zuul Projects'
|
||||
super.componentDidMount()
|
||||
if (this.props.tenant.name) {
|
||||
this.updateData()
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
if (this.props.tenant.name !== prevProps.tenant.name) {
|
||||
this.updateData()
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const { remoteData } = this.props
|
||||
const projects = remoteData.projects[this.props.tenant.name]
|
||||
|
||||
// TODO (felix): Can we somehow differentiate between "no projects yet" (due
|
||||
// to fetching) and "no projects at all", so we could show an empty state
|
||||
// in the latter case. The same applies for other pages like labels, nodes,
|
||||
// buildsets, ... as well.
|
||||
if (!projects) {
|
||||
return (<p>Loading...</p>)
|
||||
return <Fetching />
|
||||
}
|
||||
|
||||
const headerFormat = value => <Table.Heading>{value}</Table.Heading>
|
||||
|
@ -86,9 +98,12 @@ class ProjectsPage extends Refreshable {
|
|||
})
|
||||
return (
|
||||
<PageSection variant={PageSectionVariants.light}>
|
||||
<div style={{float: 'right'}}>
|
||||
{this.renderSpinner()}
|
||||
</div>
|
||||
<PageSection style={{paddingRight: '5px'}}>
|
||||
<Fetchable
|
||||
isFetching={remoteData.isFetching}
|
||||
fetchCallback={this.updateData}
|
||||
/>
|
||||
</PageSection>
|
||||
<Table.PfProvider
|
||||
striped
|
||||
bordered
|
||||
|
|
|
@ -27,10 +27,10 @@ import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
|||
|
||||
import { fetchStatusIfNeeded } from '../actions/status'
|
||||
import Pipeline from '../containers/status/Pipeline'
|
||||
import Refreshable from '../containers/Refreshable'
|
||||
import { Fetchable } from '../containers/Fetching'
|
||||
|
||||
|
||||
class StatusPage extends Refreshable {
|
||||
class StatusPage extends React.Component {
|
||||
static propTypes = {
|
||||
location: PropTypes.object,
|
||||
tenant: PropTypes.object,
|
||||
|
@ -96,10 +96,18 @@ class StatusPage extends Refreshable {
|
|||
componentDidMount () {
|
||||
document.title = 'Zuul Status'
|
||||
this.loadState()
|
||||
super.componentDidMount()
|
||||
if (this.props.tenant.name) {
|
||||
this.updateData()
|
||||
}
|
||||
window.addEventListener('storage', this.loadState)
|
||||
}
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
if (this.props.tenant.name !== prevProps.tenant.name) {
|
||||
this.updateData()
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
if (this.timer) {
|
||||
clearTimeout(this.timer)
|
||||
|
@ -214,12 +222,15 @@ class StatusPage extends Refreshable {
|
|||
)
|
||||
return (
|
||||
<PageSection variant={PageSectionVariants.light}>
|
||||
<div className="pull-right" style={{display: 'flex'}}>
|
||||
{this.renderSpinner()}
|
||||
<div style={{display: 'flex', float: 'right'}}>
|
||||
<Fetchable
|
||||
isFetching={remoteData.isFetching}
|
||||
fetchCallback={this.updateData}
|
||||
/>
|
||||
<Checkbox
|
||||
defaultChecked={autoReload}
|
||||
onChange={(e) => {this.setState({autoReload: e.target.checked})}}
|
||||
style={{marginTop: '0px'}}>
|
||||
style={{marginTop: '0px', marginLeft: '10px'}}>
|
||||
auto reload
|
||||
</Checkbox>
|
||||
</div>
|
||||
|
|
|
@ -19,11 +19,11 @@ import { Link } from 'react-router-dom'
|
|||
import { Table } from 'patternfly-react'
|
||||
import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
||||
|
||||
import Refreshable from '../containers/Refreshable'
|
||||
import { Fetching } from '../containers/Fetching'
|
||||
import { fetchTenantsIfNeeded } from '../actions/tenants'
|
||||
|
||||
|
||||
class TenantsPage extends Refreshable {
|
||||
class TenantsPage extends React.Component {
|
||||
static propTypes = {
|
||||
remoteData: PropTypes.object,
|
||||
dispatch: PropTypes.func
|
||||
|
@ -43,6 +43,10 @@ class TenantsPage extends Refreshable {
|
|||
|
||||
render () {
|
||||
const { remoteData } = this.props
|
||||
if (remoteData.isFetching) {
|
||||
return <Fetching />
|
||||
}
|
||||
|
||||
const tenants = remoteData.tenants
|
||||
const headerFormat = value => <Table.Heading>{value}</Table.Heading>
|
||||
const cellFormat = (value) => (
|
||||
|
@ -79,9 +83,6 @@ class TenantsPage extends Refreshable {
|
|||
})
|
||||
return (
|
||||
<PageSection variant={PageSectionVariants.light}>
|
||||
<div style={{float: 'right'}}>
|
||||
{this.renderSpinner()}
|
||||
</div>
|
||||
<Table.PfProvider
|
||||
striped
|
||||
bordered
|
||||
|
|
Loading…
Reference in New Issue