diff --git a/client/src/components/job-lifecycle/job-lifecycle.component.jsx b/client/src/components/job-lifecycle/job-lifecycle.component.jsx index 8122229af..62ecb920a 100644 --- a/client/src/components/job-lifecycle/job-lifecycle.component.jsx +++ b/client/src/components/job-lifecycle/job-lifecycle.component.jsx @@ -1,139 +1,110 @@ -import {createStructuredSelector} from "reselect"; -import {selectBodyshop} from "../../redux/user/user.selectors"; -import {connect} from "react-redux"; -import {useCallback, useEffect, useState} from "react"; -import axios from "axios"; -import {Card, Space, Table, Timeline} from "antd"; -import {Cell, LabelList, Legend, Pie, PieChart, Tooltip} from "recharts"; +import React, {useEffect, useMemo, useState} from 'react'; +import axios from 'axios'; +import {Card, Space, Table, Timeline} from 'antd'; +import {Bar, BarChart, CartesianGrid, LabelList, Legend, ResponsiveContainer, Tooltip, XAxis, YAxis} from 'recharts'; -const mapStateToProps = createStructuredSelector({ - bodyshop: selectBodyshop, -}); -const mapDispatchToProps = (dispatch) => ({ - //setUserLanguage: language => dispatch(setUserLanguage(language)) -}); +export function JobLifecycleComponent({job, ...rest}) { -const COLORS = ['#0088FE', '#00C49F', '#FFBB28', '#FF8042', '#8884d8']; - -export function JobLifecycleComponent({bodyshop, job, ...rest}) { const [loading, setLoading] = useState(true); const [lifecycleData, setLifecycleData] = useState(null); - useEffect(() => { - async function getLifecycleData() { + const getLifecycleData = async () => { if (job && job.id) { - setLoading(true); - const response = await axios.post("/job/lifecycle", { - jobids: job.id, - }); - const data = response.data.transition[job.id]; - setLifecycleData(data); - setLoading(false); + 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); + } } - } + }; - getLifecycleData().catch((err) => { - console.log(`Something went wrong getting Job Lifecycle Data: ${err.message}`); - setLoading(false); - }); + getLifecycleData(); }, [job]); - // // TODO - Delete this useEffect, it is for testing - // useEffect(() => { - // console.dir(lifecycleData) - // }, [lifecycleData]); - const columnKeys = [ - 'start', - 'end', - 'value', - 'prev_value', - 'next_value', - 'duration', - 'type', - 'created_at', - 'updated_at', - 'start_readable', - 'end_readable', + 'start', 'end', 'value', 'prev_value', 'next_value', 'duration', 'type', 'created_at', 'updated_at', 'start_readable', 'end_readable','duration' ]; const columns = columnKeys.map(key => ({ - title: key.charAt(0).toUpperCase() + key.slice(1), // Capitalize the first letter for the title + title: key.charAt(0).toUpperCase() + key.slice(1), dataIndex: key, key: key, })); - /** - * Returns an array of cells for the Pie Chart - * @type {function(): *[]} - */ - const renderCells = useCallback(() => { - const entires = Object - .entries(lifecycleData.durations) - .filter(([name, value]) => { - return value > 0; - }) - return entires.map(([name, value], index) => ( - - - - - )); - }, [lifecycleData, job]); + const durationsData = useMemo(() => { + if (!lifecycleData) { + return []; + } - /** - * Returns an array of objects with the name and value of the duration - * @type {function(): {name: *, value}[]} - */ - const durationsData = useCallback(() => { - return Object.entries(lifecycleData.durations) .filter(([name, value]) => { - return value > 0; - }).map(([name, value]) => ({ - name, - value: value / 1000 - })) - }, [lifecycleData, job]); + const transformedData = Object.entries(lifecycleData.durations).map(([name, {value, humanReadable}]) => { + return { + name, + amt: value, + pv: humanReadable, + uv: value, + } + }) + + return [transformedData]; + }, [lifecycleData]); + + + useEffect(() => { + console.dir(lifecycleData, {depth: null}) + console.dir(durationsData, {depth: null}) + + }, [lifecycleData,durationsData]); return ( {!loading ? ( lifecycleData ? ( - - - {lifecycleData.lifecycle.map((item, index) => ( - + {item.value} - {item.start_readable} ))} - - `${name}: ${(percent * 100).toFixed(0)}%`} - outerRadius={80} - fill="#8884d8" - dataKey="value" + + - {renderCells()} - - - - + + + + + + + + + - + +
+ ) : ( @@ -149,4 +120,4 @@ export function JobLifecycleComponent({bodyshop, job, ...rest}) { ); } -export default connect(mapStateToProps, mapDispatchToProps)(JobLifecycleComponent); +export default JobLifecycleComponent; \ No newline at end of file diff --git a/client/src/pages/jobs-detail/jobs-detail.page.component.jsx b/client/src/pages/jobs-detail/jobs-detail.page.component.jsx index a7d51d5e3..0f335d982 100644 --- a/client/src/pages/jobs-detail/jobs-detail.page.component.jsx +++ b/client/src/pages/jobs-detail/jobs-detail.page.component.jsx @@ -294,7 +294,7 @@ export function JobsDetailPage({ tab={Lifecycle} key="lifecycle" > - + { let statusDuration = {}; transitions.forEach((transition, index) => { - let duration = transition.duration; + let duration = transition.duration_minutes; // If there is no prev_value, it is the first transition if (!transition.prev_value) { - statusDuration[transition.value] = duration; + statusDuration[transition.value] = { + value: duration, + humanReadable: transition.duration_readable + }; } // If there is no next_value, it is the last transition (the active one) else if (!transition.next_value) { if (statusDuration[transition.value]) { - statusDuration[transition.value] += duration; + statusDuration[transition.value].value += duration; + statusDuration[transition.value].humanReadable = transition.duration_readable; } else { - statusDuration[transition.value] = duration; + statusDuration[transition.value] = { + value: duration, + humanReadable: transition.duration_readable + }; } } // For all other transitions else { if (statusDuration[transition.value]) { - statusDuration[transition.value] += duration; + statusDuration[transition.value].value += duration; + statusDuration[transition.value].humanReadable = transition.duration_readable; } else { - statusDuration[transition.value] = duration; + statusDuration[transition.value] = { + value: duration, + humanReadable: transition.duration_readable + }; } } }); @@ -53,7 +64,6 @@ const jobLifecycle = async (req, res) => { } - const transitionsByJobId = _.groupBy(resp.transitions, 'jobid'); const groupedTransitions = {}; @@ -66,6 +76,11 @@ const jobLifecycle = async (req, res) => { if (transition.end) { transition.end_readable = moment(transition.end).fromNow(); } + if(transition.duration){ + transition.duration_seconds = Math.round(transition.duration / 1000); + transition.duration_minutes = Math.round(transition.duration_seconds / 60); + transition.duration_readable = moment.duration(transition.duration).humanize(); + } return transition; }); @@ -75,7 +90,7 @@ const jobLifecycle = async (req, res) => { }; } - console.dir(groupedTransitions, {depth: null}); + console.dir(groupedTransitions, {depth: null}) return res.status(200).json({ jobIDs,