summaryrefslogtreecommitdiff
path: root/web/src/containers/charts/GanttChart.jsx
blob: 5ac065fce3b74d8167d1f5058cd0cf451eeeb8ec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// Copyright 2021 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.


import * as React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'

import * as moment from 'moment'
import 'moment-duration-format'

import { Chart, ChartBar, ChartAxis, ChartLegend, ChartTooltip } from '@patternfly/react-charts'

import { buildResultLegendData, buildsBarStyle } from './Misc'


function BuildsetGanttChart(props) {
    const { builds, timezone } = props
    const sortedByStartTime = builds.sort((a, b) => {
        if (a.start_time > b.start_time) {
            return -1
        }
        if (a.start_time < b.start_time) {
            return 1
        }
        return 0
    })
    const origin = moment.utc(sortedByStartTime[builds.length - 1].start_time).tz(timezone)

    const longestJobName = builds.reduce((a, build) => (a.length < build.job_name.length ? build.job_name : a), '')

    const data = sortedByStartTime.map((build) => {
        return {
            x: build.job_name,
            y0: build.start_time ? (moment.utc(build.start_time).tz(timezone) - origin) / 1000 : 0,
            y: build.end_time ? (moment.utc(build.end_time).tz(timezone) - origin) / 1000 : 0,
            result: build.result,
            started: moment.utc(build.start_time).tz(timezone).format('YYYY-MM-DD HH:mm:ss'),
            ended: moment.utc(build.end_time).tz(timezone).format('YYYY-MM-DD HH:mm:ss'),
        }
    })

    const legendData = builds.map((build) => (
        build.result
    )).filter((result, idx, self) => { return self.indexOf(result) === idx }
    ).map((legend) => ({ name: legend }))

    const uniqueResults = builds.map(
        (build) => (build.result)
    ).filter((result, idx, self) => {
        return self.indexOf(result) === idx
    })

    const chartLegend = buildResultLegendData.filter((legend) => { return uniqueResults.indexOf(legend.name) > -1 })


    return (
        <div style={{ height: Math.max(400, 20 * builds.length) + 'px', width: '900px' }}>
            <Chart
                horizontal
                domainPadding={{ x: 20 }}
                width={750}
                height={Math.max(400, 20 * builds.length)}
                padding={{
                    bottom: 80,
                    left: 8 * longestJobName.length,
                    right: 80,
                    top: 80,
                }}
                legendOrientation='horizontal'
                legendPosition='top'
                legendData={legendData}
                legendComponent={<ChartLegend data={chartLegend} itemsPerRow={4} />}

            >
                <ChartAxis />
                <ChartAxis
                    dependentAxis
                    showGrid
                    tickFormat={(t) => {
                        let format
                        switch (true) {
                            case (t < 180):
                                format = 's [sec]'
                                break
                            case (t < 7200):
                                format = 'm [min]'
                                break
                            default:
                                format = 'h [hr] m [min]'
                        }
                        return moment.duration(t, 'seconds').format(format)
                    }}
                    fixLabelOverlap={true}
                    style={{ tickLabels: { angle: -25, padding: 1, verticalAnchor: 'middle', textAnchor: 'end' } }} />
                <ChartBar
                    data={data}
                    style={buildsBarStyle}
                    labelComponent={
                        <ChartTooltip constrainToVisibleArea />}
                    labels={({ datum }) => `${datum.result}\nStarted ${datum.started}\nEnded ${datum.ended}`}
                />
            </ Chart>
        </div>
    )

}

BuildsetGanttChart.propTypes = {
    builds: PropTypes.array.isRequired,
    timezone: PropTypes.string,
}

export default connect((state) => ({
    timezone: state.timezone,
}))(BuildsetGanttChart)