2e4b605722
Change the "duration" colubm header in the buildset table to a dropdown which allows the user to display either the buildset duration (end-start) or overall duration (end-trigger event). Also update the label on the buildset page to say "Buildset duration" to match. Change-Id: Ib208bcf6b9673d2bba52de90ea6682ec650986a9
297 lines
8.4 KiB
JavaScript
297 lines
8.4 KiB
JavaScript
// 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 React from 'react'
|
|
import PropTypes from 'prop-types'
|
|
import { connect } from 'react-redux'
|
|
import {
|
|
Button,
|
|
Dropdown,
|
|
DropdownItem,
|
|
DropdownPosition,
|
|
DropdownToggle,
|
|
EmptyState,
|
|
EmptyStateBody,
|
|
EmptyStateIcon,
|
|
EmptyStateSecondaryActions,
|
|
Spinner,
|
|
Title,
|
|
} from '@patternfly/react-core'
|
|
import {
|
|
BuildIcon,
|
|
CodeBranchIcon,
|
|
CodeIcon,
|
|
CogIcon,
|
|
CubeIcon,
|
|
OutlinedCalendarAltIcon,
|
|
OutlinedClockIcon,
|
|
PollIcon,
|
|
StreamIcon,
|
|
} from '@patternfly/react-icons'
|
|
import {
|
|
Table,
|
|
TableHeader,
|
|
TableBody,
|
|
TableVariant,
|
|
truncate,
|
|
breakWord,
|
|
cellWidth,
|
|
} from '@patternfly/react-table'
|
|
import 'moment-duration-format'
|
|
import * as moment from 'moment'
|
|
|
|
import { BuildResult, BuildResultWithIcon } from './Misc'
|
|
import { buildExternalTableLink, IconProperty } from '../../Misc'
|
|
|
|
function BuildsetTable({
|
|
buildsets,
|
|
fetching,
|
|
onClearFilters,
|
|
tenant,
|
|
timezone,
|
|
history,
|
|
}) {
|
|
const [isDurationOpen, setIsDurationOpen] = React.useState(false)
|
|
const [currentDuration, setCurrentDuration] = React.useState(
|
|
'Buildset Duration'
|
|
)
|
|
|
|
const handleDurationSelect = (event) => {
|
|
setIsDurationOpen(!isDurationOpen)
|
|
setCurrentDuration(event.target.innerText)
|
|
}
|
|
|
|
const columns = [
|
|
{
|
|
title: <IconProperty icon={<CubeIcon />} value="Project" />,
|
|
dataLabel: 'Project',
|
|
cellTransforms: [breakWord],
|
|
},
|
|
{
|
|
title: <IconProperty icon={<CodeBranchIcon />} value="Branch" />,
|
|
dataLabel: 'Branch',
|
|
cellTransforms: [breakWord],
|
|
},
|
|
{
|
|
title: <IconProperty icon={<StreamIcon />} value="Pipeline" />,
|
|
dataLabel: 'Pipeline',
|
|
cellTransforms: [breakWord],
|
|
},
|
|
{
|
|
title: <IconProperty icon={<CodeIcon />} value="Change" />,
|
|
dataLabel: 'Change',
|
|
transforms: [cellWidth(10)],
|
|
cellTransforms: [truncate],
|
|
},
|
|
{
|
|
title: <Dropdown
|
|
isText
|
|
isPlain
|
|
position={DropdownPosition.left}
|
|
onSelect={handleDurationSelect}
|
|
toggle={
|
|
<DropdownToggle
|
|
onToggle={next => setIsDurationOpen(next)}
|
|
// Use a gear instead of a caret which looks likea sort indicator.
|
|
toggleIndicator={CogIcon}
|
|
style={{
|
|
padding: 0,
|
|
fontWeight: 'var(--pf-c-table--cell-FontWeight)',
|
|
fontSize: 'var(--pf-c-table--cell-FontSize)',
|
|
color: 'var(--pf-c-table--cell-Color)'
|
|
}}
|
|
id="toggle-id-duration"
|
|
>
|
|
<OutlinedClockIcon /> {currentDuration}
|
|
</DropdownToggle>
|
|
}
|
|
isOpen={isDurationOpen}
|
|
dropdownItems={[
|
|
<DropdownItem key="buildset">Buildset Duration</DropdownItem>,
|
|
<DropdownItem key="overall">Overall Duration</DropdownItem>,
|
|
]}
|
|
style={{ width: '100%', padding: 0 }}
|
|
/>,
|
|
dataLabel: 'Duration',
|
|
props: {style: {overflowY: 'visible'}},
|
|
},
|
|
{
|
|
title: (
|
|
<IconProperty icon={<OutlinedCalendarAltIcon />} value="Start time" />
|
|
),
|
|
dataLabel: 'Start time',
|
|
},
|
|
{
|
|
title: <IconProperty icon={<PollIcon />} value="Result" />,
|
|
dataLabel: 'Result',
|
|
},
|
|
]
|
|
|
|
function createBuildsetRow(buildset) {
|
|
const changeOrRefLink = buildExternalTableLink(buildset)
|
|
|
|
let duration
|
|
if (currentDuration === 'Buildset Duration') {
|
|
duration = moment.utc(buildset.last_build_end_time) -
|
|
moment.utc(buildset.first_build_start_time)
|
|
} else {
|
|
duration = moment.utc(buildset.last_build_end_time) -
|
|
moment.utc(buildset.event_timestamp)
|
|
}
|
|
|
|
return {
|
|
// Pass the buildset's uuid as row id, so we can use it later on in the
|
|
// action handler to build the link to the build result page for each row.
|
|
id: buildset.uuid,
|
|
cells: [
|
|
{
|
|
// To allow passing anything else than simple string values to a table
|
|
// cell, we must use the title attribute.
|
|
title: (
|
|
<BuildResultWithIcon
|
|
result={buildset.result}
|
|
link={`${tenant.linkPrefix}/buildset/${buildset.uuid}`}>
|
|
{buildset.project}
|
|
</BuildResultWithIcon>
|
|
),
|
|
},
|
|
{
|
|
title: buildset.branch ? buildset.branch : buildset.ref,
|
|
},
|
|
{
|
|
title: buildset.pipeline,
|
|
},
|
|
{
|
|
title: changeOrRefLink && changeOrRefLink,
|
|
},
|
|
{
|
|
title: moment
|
|
.duration(duration, 'ms')
|
|
.format('h [hr] m [min] s [sec]'),
|
|
},
|
|
{
|
|
title: moment
|
|
.utc(buildset.first_build_start_time)
|
|
.tz(timezone)
|
|
.format('YYYY-MM-DD HH:mm:ss'),
|
|
},
|
|
{
|
|
title: (
|
|
<BuildResult
|
|
result={buildset.result}
|
|
link={`${tenant.linkPrefix}/buildset/${buildset.uuid}`}
|
|
>
|
|
</BuildResult>
|
|
),
|
|
},
|
|
],
|
|
}
|
|
}
|
|
|
|
function createFetchingRow() {
|
|
const rows = [
|
|
{
|
|
heightAuto: true,
|
|
cells: [
|
|
{
|
|
props: { colSpan: 8 },
|
|
title: (
|
|
<center>
|
|
<Spinner size="xl" />
|
|
</center>
|
|
),
|
|
},
|
|
],
|
|
},
|
|
]
|
|
return rows
|
|
}
|
|
|
|
let rows = []
|
|
// For the fetching row we don't need any actions, so we keep them empty by
|
|
// default.
|
|
let actions = []
|
|
if (fetching) {
|
|
rows = createFetchingRow()
|
|
// The dataLabel property is used to show the column header in a list-like
|
|
// format for smaller viewports. When we are fetching, we don't want the
|
|
// fetching row to be prepended by a "Project" column header. The other
|
|
// column headers are not relevant here since we only have a single cell in
|
|
// the fetching row.
|
|
columns[0].dataLabel = ''
|
|
} else {
|
|
rows = buildsets.map((buildset) => createBuildsetRow(buildset))
|
|
// This list of actions will be applied to each row in the table. For
|
|
// row-specific actions we must evaluate the individual row data provided to
|
|
// the onClick handler.
|
|
actions = [
|
|
{
|
|
title: 'Show buildset result',
|
|
onClick: (event, rowId, rowData) =>
|
|
// The row's id contains the buildset's uuid, so we can use it to
|
|
// build the correct link.
|
|
history.push(`${tenant.linkPrefix}/buildset/${rowData.id}`),
|
|
},
|
|
]
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<Table
|
|
aria-label="Builds Table"
|
|
variant={TableVariant.compact}
|
|
cells={columns}
|
|
rows={rows}
|
|
actions={actions}
|
|
className="zuul-table"
|
|
>
|
|
<TableHeader />
|
|
<TableBody />
|
|
</Table>
|
|
|
|
{/* Show an empty state in case we don't have any buildsets but are also
|
|
not fetching */}
|
|
{!fetching && buildsets.length === 0 && (
|
|
<EmptyState>
|
|
<EmptyStateIcon icon={BuildIcon} />
|
|
<Title headingLevel="h1">No buildsets found</Title>
|
|
<EmptyStateBody>
|
|
No buildsets match this filter criteria. Remove some filters or
|
|
clear all to show results.
|
|
</EmptyStateBody>
|
|
<EmptyStateSecondaryActions>
|
|
<Button variant="link" onClick={onClearFilters}>
|
|
Clear all filters
|
|
</Button>
|
|
</EmptyStateSecondaryActions>
|
|
</EmptyState>
|
|
)}
|
|
</>
|
|
)
|
|
}
|
|
|
|
BuildsetTable.propTypes = {
|
|
buildsets: PropTypes.array.isRequired,
|
|
fetching: PropTypes.bool.isRequired,
|
|
onClearFilters: PropTypes.func.isRequired,
|
|
tenant: PropTypes.object.isRequired,
|
|
timezone: PropTypes.string.isRequired,
|
|
history: PropTypes.object.isRequired,
|
|
}
|
|
|
|
export default connect((state) => ({
|
|
tenant: state.tenant,
|
|
timezone: state.timezone,
|
|
}))(BuildsetTable)
|