// 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 { connect } from 'react-redux'
import { Link } from 'react-router-dom'
import {
DataList,
DataListCell,
DataListItem,
DataListItemRow,
DataListItemCells,
Flex,
FlexItem,
Switch,
} from '@patternfly/react-core'
import {
AngleDownIcon,
AngleRightIcon,
OutlinedClockIcon
} from '@patternfly/react-icons'
import 'moment-duration-format'
import * as moment from 'moment'
import { BuildResult, BuildResultWithIcon } from './Misc'
import { IconProperty } from '../../Misc'
class BuildList extends React.Component {
static propTypes = {
builds: PropTypes.array,
tenant: PropTypes.object,
}
// TODO (felix): Add a property "isCompact" to be used on the buildresult
// page. Without this flag we might then even use this (with more
// information) on the /builds page.
constructor(props) {
super(props)
const { builds } = this.props
let retriedJobs = builds.filter((build) => {
return !build.final
}).map((build) => (build.job_name)
).filter((build, idx, self) => {
return self.indexOf(build) === idx
})
let skippedJobs = builds.filter((build) => {
return build.result === 'SKIPPED'
}).map((build) => (build.job_name)
).filter((build, idx, self) => {
return self.indexOf(build) === idx
})
this.state = {
visibleNonFinalBuilds: retriedJobs,
retriedJobs: retriedJobs,
skippedJobs: skippedJobs,
showSkipped: false,
}
}
sortedBuilds = () => {
const { builds } = this.props
const { visibleNonFinalBuilds, showSkipped } = this.state
return builds.sort((a, b) => {
// Group builds by job name, then order by decreasing start time; this will ensure retries are together
if (a.job_name === b.job_name) {
if (a.start_time < b.start_time) {
return 1
}
if (a.start_time > b.start_time) {
return -1
}
return 0
}
if (a.job_name > b.job_name) {
return 1
} else {
return -1
}
}).filter((build) => {
if (build.final || visibleNonFinalBuilds.indexOf(build.job_name) >= 0) {
if (build.result === 'SKIPPED' && !showSkipped) {
return false
}
return true
}
else {
return false
}
})
}
handleFinalSwitch = isChecked => {
const { retriedJobs } = this.state
this.setState({ visibleNonFinalBuilds: (isChecked ? retriedJobs : []) })
}
handleSkippedSwitch = isChecked => {
this.setState({ showSkipped: isChecked })
}
handleToggleVisibleNonFinalBuilds = (jobName) => {
const { visibleNonFinalBuilds } = this.state
const index = visibleNonFinalBuilds.indexOf(jobName)
const newVisible =
index >= 0 ? [...visibleNonFinalBuilds.slice(0, index), ...visibleNonFinalBuilds.slice(index + 1, visibleNonFinalBuilds.length)] : [...visibleNonFinalBuilds, jobName]
this.setState({
visibleNonFinalBuilds: newVisible,
})
}
renderRetriesButton = (build, hasRetries) => {
const { visibleNonFinalBuilds } = this.state
if (!build.final || !hasRetries) {
return
{/* Hide the icon to maintain alignment between final and non-final elements */}
}
const isExpanded = (visibleNonFinalBuilds.indexOf(build.job_name) >= 0)
const RetryIcon =
isExpanded
? AngleDownIcon
: AngleRightIcon
const retryAltText =
isExpanded
? 'Hide retries for this job'
: 'Show retries for this job'
// TODO either replace this with an ExpandableSection (but this breaks the layout) or figure out CSS animations for the icon.
return (
{ this.handleToggleVisibleNonFinalBuilds(build.job_name) }}
title={retryAltText}
style={{ cursor: 'pointer' }} />
)
}
render() {
const { tenant } = this.props
const { visibleNonFinalBuilds, retriedJobs, skippedJobs, showSkipped } = this.state
let retrySwitch = retriedJobs.length > 0 ?
Show retries :
<>>
let skippedSwitch = skippedJobs.length > 0 ?
Show skipped jobs :
<>>
const sortedBuilds = this.sortedBuilds()
return (
{skippedSwitch}
{retrySwitch}
)
}
}
export default connect((state) => ({ tenant: state.tenant }))(BuildList)