175 lines
6.1 KiB
JavaScript
175 lines
6.1 KiB
JavaScript
import React, {useCallback, useEffect, useState} from 'react';
|
|
import moment from "moment";
|
|
import axios from 'axios';
|
|
import {Card, Space, Table} from 'antd';
|
|
import {gql, useQuery} from "@apollo/client";
|
|
import {DateTimeFormatterFunction} from "../../utils/DateFormatter";
|
|
import {isEmpty} from "lodash";
|
|
import {Bar, BarChart, CartesianGrid, Legend, ResponsiveContainer, Tooltip, XAxis, YAxis} from "recharts";
|
|
|
|
const transformDataForChart = (durations) => {
|
|
const output = {};
|
|
output.total = durations.total;
|
|
return durations.summations.forEach((summation) => {
|
|
output[summation.status] = summation.value
|
|
});
|
|
};
|
|
const getColor = (key) => {
|
|
// Generate a random color
|
|
const randomColor = '#' + Math.floor(Math.random()*16777215).toString(16);
|
|
return randomColor;
|
|
};
|
|
|
|
|
|
export function JobLifecycleComponent({job, ...rest}) {
|
|
|
|
const [loading, setLoading] = useState(true);
|
|
const [lifecycleData, setLifecycleData] = useState(null);
|
|
|
|
// Used for tracking external state changes.
|
|
const {data} = useQuery(gql`
|
|
query get_job_test($id: uuid!){
|
|
jobs_by_pk(id:$id){
|
|
id
|
|
status
|
|
}
|
|
}
|
|
`, {
|
|
variables: {
|
|
id: job.id
|
|
},
|
|
fetchPolicy: 'cache-only'
|
|
});
|
|
|
|
/**
|
|
* Gets the lifecycle data for the job.
|
|
* @returns {Promise<void>}
|
|
*/
|
|
const getLifecycleData = useCallback(async () => {
|
|
if (job && job.id) {
|
|
try {
|
|
setLoading(true);
|
|
const response = await axios.post("/job/lifecycle", {jobids: job.id});
|
|
const data = response.data.transition[job.id];
|
|
setLifecycleData(data);
|
|
} catch (err) {
|
|
console.error(`Error getting Job Lifecycle Data: ${err.message}`);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}
|
|
}, [job]);
|
|
|
|
useEffect(() => {
|
|
if (!data) return;
|
|
setTimeout(() => {
|
|
getLifecycleData().catch(err => console.error(`Error getting Job Lifecycle Data: ${err.message}`));
|
|
}, 1000);
|
|
}, [data, getLifecycleData]);
|
|
|
|
const columns = [
|
|
{
|
|
title: 'Value',
|
|
dataIndex: 'value',
|
|
key: 'value',
|
|
},
|
|
{
|
|
title: 'Start',
|
|
dataIndex: 'start',
|
|
key: 'start',
|
|
render: (text) => DateTimeFormatterFunction(text),
|
|
sorter: (a, b) => moment(a.start).unix() - moment(b.start).unix(),
|
|
},
|
|
{
|
|
title: 'Relative Start',
|
|
dataIndex: 'start_readable',
|
|
key: 'start_readable',
|
|
},
|
|
{
|
|
title: 'End',
|
|
dataIndex: 'end',
|
|
key: 'end',
|
|
sorter: (a, b) => {
|
|
if (isEmpty(a.end) || isEmpty(b.end)) {
|
|
if (isEmpty(a.end) && isEmpty(b.end)) {
|
|
return 0;
|
|
}
|
|
return isEmpty(a.end) ? 1 : -1;
|
|
}
|
|
return moment(a.end).unix() - moment(b.end).unix();
|
|
},
|
|
render: (text) => isEmpty(text) ? 'N/A' : DateTimeFormatterFunction(text)
|
|
},
|
|
{
|
|
title: 'Relative End',
|
|
dataIndex: 'end_readable',
|
|
key: 'end_readable',
|
|
},
|
|
{
|
|
title: 'Duration',
|
|
dataIndex: 'duration_readable',
|
|
key: 'duration_readable',
|
|
sorter: (a, b) => a.duration - b.duration,
|
|
},
|
|
];
|
|
|
|
useEffect(() => {
|
|
console.log('LifeCycle Data');
|
|
console.dir(lifecycleData, {depth: null})
|
|
}, [lifecycleData]);
|
|
|
|
return (
|
|
<Card loading={loading} title='Job Lifecycle Component'>
|
|
{!loading ? (
|
|
lifecycleData && lifecycleData.lifecycle && lifecycleData.durations ? (
|
|
<Space direction='vertical' style={{width: '100%'}}>
|
|
<Space direction='horizontal' style={{width: '100%'}} align='start'>
|
|
<Card type='inner' title='Durations'>
|
|
<ResponsiveContainer width="100%" height="100%">
|
|
<BarChart
|
|
width={500}
|
|
height={300}
|
|
data={transformDataForChart(lifecycleData.durations)}
|
|
margin={{
|
|
top: 20,
|
|
right: 30,
|
|
left: 20,
|
|
bottom: 5,
|
|
}}
|
|
>
|
|
<CartesianGrid strokeDasharray="3 3" />
|
|
<XAxis dataKey="name" />
|
|
<YAxis />
|
|
<Tooltip />
|
|
<Legend />
|
|
{lifecycleData.durations.summations.map((summation, idx) => {
|
|
|
|
return (
|
|
<Bar key={idx} dataKey={summation.status} stackId="a" fill={getColor(summation.status)} />
|
|
);
|
|
})}
|
|
|
|
</BarChart>
|
|
</ResponsiveContainer>
|
|
|
|
</Card>
|
|
</Space>
|
|
<Card type='inner' title='Transitions'>
|
|
<Table columns={columns} dataSource={lifecycleData.lifecycle}/>
|
|
</Card>
|
|
</Space>
|
|
) : (
|
|
<Card type='inner' style={{textAlign: 'center'}}>
|
|
There is currently no lifecycle data for this job.
|
|
</Card>
|
|
)
|
|
) : (
|
|
<Card type='inner' title='Loading'>
|
|
Loading Job Timelines....
|
|
</Card>
|
|
)}
|
|
</Card>
|
|
);
|
|
}
|
|
|
|
export default JobLifecycleComponent; |