IO-1933 Added statistics on tech page
This commit is contained in:
@@ -241,9 +241,11 @@ export default function ScoreboardTimeTickets() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
ret.totalEffieciencyOverPeriod =
|
ret.totalEffieciencyOverPeriod =
|
||||||
(totalActualAndProductive.totalOverPeriod /
|
totalActualAndProductive.actualTotalOverPeriod
|
||||||
totalActualAndProductive.actualTotalOverPeriod) *
|
? (totalActualAndProductive.totalOverPeriod /
|
||||||
100;
|
totalActualAndProductive.actualTotalOverPeriod) *
|
||||||
|
100
|
||||||
|
: 0;
|
||||||
|
|
||||||
roundObject(ret);
|
roundObject(ret);
|
||||||
roundObject(totals);
|
roundObject(totals);
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ export function ScoreboardTicketsStats({ data, bodyshop }) {
|
|||||||
<Col span={12}>
|
<Col span={12}>
|
||||||
<Statistic
|
<Statistic
|
||||||
title={t("scoreboard.labels.efficiencyoverperiod")}
|
title={t("scoreboard.labels.efficiencyoverperiod")}
|
||||||
value={`${data.totalEffieciencyOverPeriod}%`}
|
value={`${data.totalEffieciencyOverPeriod || 0}%`}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|||||||
@@ -0,0 +1,136 @@
|
|||||||
|
import { Card, Col, Space, Statistic, Typography } from "antd";
|
||||||
|
import { useLocation } from "react-router-dom";
|
||||||
|
import queryString from "query-string";
|
||||||
|
import moment from "moment";
|
||||||
|
import { useQuery } from "@apollo/client";
|
||||||
|
import { QUERY_TIME_TICKETS_TECHNICIAN_IN_RANGE } from "../../graphql/timetickets.queries";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import AlertComponent from "../alert/alert.component";
|
||||||
|
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||||
|
import { useMemo } from "react";
|
||||||
|
|
||||||
|
const { Title } = Typography;
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
technician: selectTechnician,
|
||||||
|
});
|
||||||
|
const mapDispatchToProps = (dispatch) => ({});
|
||||||
|
|
||||||
|
const TechJobStatistics = ({ technician }) => {
|
||||||
|
const searchParams = queryString.parse(useLocation().search);
|
||||||
|
const { start, end } = searchParams;
|
||||||
|
|
||||||
|
const startDate = start
|
||||||
|
? moment(start)
|
||||||
|
: moment().startOf("week").subtract(7, "days");
|
||||||
|
const endDate = end ? moment(end) : moment().endOf("week");
|
||||||
|
|
||||||
|
const { loading, error, data } = useQuery(
|
||||||
|
QUERY_TIME_TICKETS_TECHNICIAN_IN_RANGE,
|
||||||
|
{
|
||||||
|
variables: {
|
||||||
|
start: startDate.format("YYYY-MM-DD"),
|
||||||
|
end: endDate.format("YYYY-MM-DD"),
|
||||||
|
fixedStart: moment().startOf("month").format("YYYY-MM-DD"),
|
||||||
|
fixedEnd: moment().endOf("month").format("YYYY-MM-DD"),
|
||||||
|
employeeid: technician.id,
|
||||||
|
},
|
||||||
|
fetchPolicy: "network-only",
|
||||||
|
nextFetchPolicy: "network-only",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const totals = useMemo(() => {
|
||||||
|
if (data && data.timetickets && data.fixedperiod) {
|
||||||
|
const week = data.timetickets.reduce(
|
||||||
|
(acc, val) => {
|
||||||
|
acc.productivehrs = acc.productivehrs + val.productivehrs;
|
||||||
|
acc.actualhrs = acc.actualhrs + val.actualhrs;
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{ productivehrs: 0, actualhrs: 0 }
|
||||||
|
);
|
||||||
|
|
||||||
|
const month = data.fixedperiod.reduce(
|
||||||
|
(acc, val) => {
|
||||||
|
acc.productivehrs = acc.productivehrs + val.productivehrs;
|
||||||
|
acc.actualhrs = acc.actualhrs + val.actualhrs;
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{ productivehrs: 0, actualhrs: 0 }
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
week,
|
||||||
|
month,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
week: { productivehrs: 0, actualhrs: 0 },
|
||||||
|
month: { productivehrs: 0, actualhrs: 0 },
|
||||||
|
};
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
|
if (loading) return <LoadingSpinner />;
|
||||||
|
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card title="Productive Hours Statistics">
|
||||||
|
<Space size={100}>
|
||||||
|
<Col>
|
||||||
|
<Title level={5}>This Week</Title>
|
||||||
|
<Space size={20}>
|
||||||
|
<Statistic
|
||||||
|
title="Productive Hours"
|
||||||
|
value={totals.week.productivehrs.toFixed(2)}
|
||||||
|
/>
|
||||||
|
<Statistic
|
||||||
|
title="Actual Hours"
|
||||||
|
value={totals.week.actualhrs.toFixed(2)}
|
||||||
|
/>
|
||||||
|
<Statistic
|
||||||
|
title="Efficiency %"
|
||||||
|
value={
|
||||||
|
totals.week.actualhrs
|
||||||
|
? `${(
|
||||||
|
(totals.week.productivehrs / totals.week.actualhrs) *
|
||||||
|
100
|
||||||
|
).toFixed(2)}%`
|
||||||
|
: "0%"
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
</Col>
|
||||||
|
<Col>
|
||||||
|
<Title level={5}>This Month</Title>
|
||||||
|
<Space size={20}>
|
||||||
|
<Statistic
|
||||||
|
title="Productive Hours"
|
||||||
|
value={totals.month.productivehrs.toFixed(2)}
|
||||||
|
/>
|
||||||
|
<Statistic
|
||||||
|
title="Actual Hours"
|
||||||
|
value={totals.month.actualhrs.toFixed(2)}
|
||||||
|
/>
|
||||||
|
<Statistic
|
||||||
|
title="Efficiency %"
|
||||||
|
value={
|
||||||
|
totals.month.actualhrs
|
||||||
|
? `${(
|
||||||
|
(totals.month.productivehrs / totals.month.actualhrs) *
|
||||||
|
100
|
||||||
|
).toFixed(2)}%`
|
||||||
|
: "0%"
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
</Col>
|
||||||
|
</Space>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(TechJobStatistics);
|
||||||
@@ -59,6 +59,81 @@ export const QUERY_TIME_TICKETS_IN_RANGE = gql`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const QUERY_TIME_TICKETS_TECHNICIAN_IN_RANGE = gql`
|
||||||
|
query QUERY_TIME_TICKETS_TECHNICIAN_IN_RANGE(
|
||||||
|
$employeeid: uuid!
|
||||||
|
$start: date!
|
||||||
|
$end: date!
|
||||||
|
$fixedStart: date!
|
||||||
|
$fixedEnd: date!
|
||||||
|
) {
|
||||||
|
timetickets(
|
||||||
|
where: {
|
||||||
|
date: { _gte: $start, _lte: $end }
|
||||||
|
employeeid: { _eq: $employeeid }
|
||||||
|
}
|
||||||
|
order_by: { date: desc_nulls_first }
|
||||||
|
) {
|
||||||
|
actualhrs
|
||||||
|
ciecacode
|
||||||
|
clockoff
|
||||||
|
clockon
|
||||||
|
cost_center
|
||||||
|
created_at
|
||||||
|
date
|
||||||
|
id
|
||||||
|
rate
|
||||||
|
productivehrs
|
||||||
|
memo
|
||||||
|
jobid
|
||||||
|
flat_rate
|
||||||
|
job {
|
||||||
|
id
|
||||||
|
ro_number
|
||||||
|
}
|
||||||
|
employeeid
|
||||||
|
employee {
|
||||||
|
id
|
||||||
|
employee_number
|
||||||
|
first_name
|
||||||
|
last_name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fixedperiod: timetickets(
|
||||||
|
where: {
|
||||||
|
date: { _gte: $fixedStart, _lte: $fixedEnd }
|
||||||
|
employeeid: { _eq: $employeeid }
|
||||||
|
}
|
||||||
|
order_by: { date: desc_nulls_first }
|
||||||
|
) {
|
||||||
|
actualhrs
|
||||||
|
ciecacode
|
||||||
|
clockoff
|
||||||
|
clockon
|
||||||
|
cost_center
|
||||||
|
created_at
|
||||||
|
date
|
||||||
|
id
|
||||||
|
rate
|
||||||
|
productivehrs
|
||||||
|
memo
|
||||||
|
jobid
|
||||||
|
flat_rate
|
||||||
|
job {
|
||||||
|
id
|
||||||
|
ro_number
|
||||||
|
}
|
||||||
|
employeeid
|
||||||
|
employee {
|
||||||
|
id
|
||||||
|
employee_number
|
||||||
|
first_name
|
||||||
|
last_name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export const QUERY_TIME_TICKETS_IN_RANGE_SB = gql`
|
export const QUERY_TIME_TICKETS_IN_RANGE_SB = gql`
|
||||||
query QUERY_TIME_TICKETS_IN_RANGE_SB(
|
query QUERY_TIME_TICKETS_IN_RANGE_SB(
|
||||||
$start: date!
|
$start: date!
|
||||||
|
|||||||
@@ -2,10 +2,12 @@ import { Divider } from "antd";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import TechClockInFormContainer from "../../components/tech-job-clock-in-form/tech-job-clock-in-form.container";
|
import TechClockInFormContainer from "../../components/tech-job-clock-in-form/tech-job-clock-in-form.container";
|
||||||
import TechClockedInList from "../../components/tech-job-clocked-in-list/tech-job-clocked-in-list.component";
|
import TechClockedInList from "../../components/tech-job-clocked-in-list/tech-job-clocked-in-list.component";
|
||||||
|
import TechJobStatistics from "../../components/tech-job-statistics/tech-job-statistics.component";
|
||||||
|
|
||||||
export default function TechClockComponent() {
|
export default function TechClockComponent() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
<TechJobStatistics />
|
||||||
<TechClockInFormContainer />
|
<TechClockInFormContainer />
|
||||||
<Divider />
|
<Divider />
|
||||||
<TechClockedInList />
|
<TechClockedInList />
|
||||||
|
|||||||
Reference in New Issue
Block a user