IO-1911 Timetickets Scoreboard.

This commit is contained in:
Patrick Fic
2022-06-06 09:54:02 -07:00
parent 3feb1a3887
commit 674c06665c
12 changed files with 674 additions and 132 deletions

View File

@@ -49,11 +49,6 @@ export const ListOfDaysInCurrentMonth = () => {
};
export const ListDaysBetween = ({ start, end }) => {
console.log(
"🚀 ~ file: scoreboard-targets-table.util.js ~ line 52 ~ start, end",
start,
end
);
const days = [];
const dateStart = moment(start);
const dateEnd = moment(end);

View File

@@ -1,14 +1,12 @@
import { Card } from "antd";
import moment from "moment";
import React, { useMemo } from "react";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import {
Area,
Bar,
CartesianGrid,
ComposedChart,
Legend,
Line,
ResponsiveContainer,
Tooltip,
XAxis,
@@ -16,9 +14,7 @@ import {
} from "recharts";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import * as Utils from "../scoreboard-targets-table/scoreboard-targets-table.util";
import _ from "lodash";
import TimeTicketsDatesSelector from "../ticket-tickets-dates-selector/time-tickets-dates-selector.component";
const graphProps = {
strokeWidth: 3,
};
@@ -35,86 +31,16 @@ export default connect(
mapDispatchToProps
)(ScoreboardTicketsBar);
export function ScoreboardTicketsBar({ start, end, timetickets, bodyshop }) {
console.log(
"🚀 ~ file: scoreboard-timetickets.bar.component.jsx ~ line 39 ~ start, end,",
start,
end
);
const listOfBusDays = Utils.ListOfDaysInCurrentMonth();
const data = useMemo(() => {
const ticketsGroupedByDate = _.groupBy(timetickets, "date");
const listOfDays = Utils.ListDaysBetween({ start, end });
console.log(
"🚀 ~ file: scoreboard-timetickets.bar.component.jsx ~ line 45 ~ listOfDays",
listOfDays
);
console.log(
"🚀 ~ file: scoreboard-timetickets.bar.component.jsx ~ line 43 ~ groupedByDate",
ticketsGroupedByDate
);
const ret = [];
listOfDays.forEach((day) => {
const r = {
date: day,
total: 0,
};
});
}, [timetickets]);
// const data = listOfBusDays.reduce((acc, val) => {
// //Sum up the current day.
// const groupedbyDate = _.groupBy(data, "date");
// let dayhrs;
// if (!!sbEntriesByDate[val]) {
// dayhrs = sbEntriesByDate[val].reduce(
// (dayAcc, dayVal) => {
// return {
// bodyhrs: dayAcc.bodyhrs + dayVal.bodyhrs,
// painthrs: dayAcc.painthrs + dayVal.painthrs,
// };
// },
// { bodyhrs: 0, painthrs: 0 }
// );
// } else {
// dayhrs = {
// bodyhrs: 0,
// painthrs: 0,
// };
// }
// const theValue = {
// date: moment(val).format("D ddd"),
// paintHrs: _.round(dayhrs.painthrs, 1),
// bodyHrs: _.round(dayhrs.bodyhrs, 1),
// accTargetHrs: _.round(
// Utils.AsOfDateTargetHours(
// bodyshop.scoreboard_target.dailyBodyTarget +
// bodyshop.scoreboard_target.dailyPaintTarget,
// val
// ),
// 1
// ),
// accHrs: _.round(
// acc.length > 0
// ? acc[acc.length - 1].accHrs + dayhrs.painthrs + dayhrs.bodyhrs
// : dayhrs.painthrs + dayhrs.bodyhrs,
// 1
// ),
// };
// return [...acc, theValue];
// }, []);
export function ScoreboardTicketsBar({ data, bodyshop }) {
const { t } = useTranslation();
return (
<Card>
<Card
title={t("scoreboard.labels.productivetimeticketsoverdate")}
extra={<TimeTicketsDatesSelector />}
>
<ResponsiveContainer width="100%" height={475}>
<ComposedChart
data={data}
data={data.chartData}
margin={{ top: 20, right: 20, bottom: 20, left: 20 }}
>
<CartesianGrid stroke="#f5f5f5" />
@@ -122,34 +48,32 @@ export function ScoreboardTicketsBar({ start, end, timetickets, bodyshop }) {
<YAxis strokeWidth={graphProps.strokeWidth} />
<Tooltip />
<Legend />
<Area
{/* <Area
type="monotone"
name="Accumulated Hours"
dataKey="accHrs"
fill="lightgreen"
stroke="green"
/>
<Bar
name="Body Hours"
dataKey="bodyHrs"
stackId="day"
barSize={20}
fill="darkblue"
/>
<Bar
name="Paint Hours"
dataKey="paintHrs"
stackId="day"
barSize={20}
fill="darkred"
/>
<Line
/> */}
{data &&
data.employees.map((e, idx) => (
<Bar
key={`${e}productive`}
name={e}
dataKey={`employees.${e}.productive`}
stackId="productive"
// barSize={20}
fill={data.colors[idx]}
/>
))}
{/* <Line
name="Target Hours"
type="monotone"
dataKey="accTargetHrs"
stroke="#ff7300"
strokeWidth={graphProps.strokeWidth}
/>
/> */}
</ComposedChart>
</ResponsiveContainer>
</Card>

View File

@@ -1,12 +1,16 @@
import React from "react";
import { useLocation } from "react-router-dom";
import queryString from "query-string";
import moment from "moment";
import { useQuery } from "@apollo/client";
import { Col, Row } from "antd";
import _ from "lodash";
import moment from "moment";
import queryString from "query-string";
import React, { useMemo } from "react";
import { useLocation } from "react-router-dom";
import { QUERY_TIME_TICKETS_IN_RANGE } from "../../graphql/timetickets.queries";
import AlertComponent from "../alert/alert.component";
import TimeTicketsDatesSelector from "../../components/ticket-tickets-dates-selector/time-tickets-dates-selector.component";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
import * as Utils from "../scoreboard-targets-table/scoreboard-targets-table.util";
import ScoreboardTicketsBar from "./scoreboard-timetickets.bar.component";
import ScoreboardTicketsStats from "./scoreboard-timetickets.stats.component";
export default function ScoreboardTimeTickets() {
const searchParams = queryString.parse(useLocation().search);
@@ -15,25 +19,230 @@ export default function ScoreboardTimeTickets() {
? moment(start)
: moment().startOf("week").subtract(7, "days");
const endDate = end ? moment(end) : moment().endOf("week");
const fixedPeriods = useMemo(() => {
const endOfThisMonth = moment().endOf("month");
const startofthisMonth = moment().startOf("month");
const endOfLastmonth = moment().subtract(1, "month").endOf("month");
const startOfLastmonth = moment().subtract(1, "month").startOf("month");
const endOfThisWeek = moment().endOf("week");
const startOfThisWeek = moment().startOf("week");
const endOfLastWeek = moment().subtract(1, "week").endOf("week");
const startOfLastWeek = moment().subtract(1, "week").startOf("week");
const allDates = [
endOfThisMonth,
startofthisMonth,
endOfLastmonth,
startOfLastmonth,
endOfThisWeek,
startOfThisWeek,
endOfLastWeek,
startOfLastWeek,
];
const start = moment.min(allDates);
const end = moment.max(allDates);
return {
start,
end,
endOfThisMonth,
startofthisMonth,
endOfLastmonth,
startOfLastmonth,
endOfThisWeek,
startOfThisWeek,
endOfLastWeek,
startOfLastWeek,
};
}, []);
const { loading, error, data } = useQuery(QUERY_TIME_TICKETS_IN_RANGE, {
variables: {
start: startDate,
end: endDate,
start: startDate.format("YYYY-MM-DD"),
end: endDate.format("YYYY-MM-DD"),
fixedStart: fixedPeriods.start.format("YYYY-MM-DD"),
fixedEnd: fixedPeriods.end.format("YYYY-MM-DD"),
},
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
pollInterval: 60000,
skip: !fixedPeriods,
});
if (error) return <AlertComponent message={error.message} type="error" />;
const calculatedData = useMemo(() => {
if (!data) return [];
const ret = {
totalThisWeek: 0,
totalLastWeek: 0,
totalThisMonth: 0,
totalLastMonth: 0,
totalOverPeriod: 0,
employees: {},
};
data.fixedperiod.forEach((ticket) => {
const ticketDate = moment(ticket.date);
if (!ret.employees[ticket.employee.employee_number]) {
ret.employees[ticket.employee.employee_number] = {
totalThisWeek: 0,
totalLastWeek: 0,
totalThisMonth: 0,
totalLastMonth: 0,
totalOverPeriod: 0,
};
}
if (
ticketDate.isBetween(
fixedPeriods.startOfThisWeek,
fixedPeriods.endOfThisWeek,
undefined,
"[]"
)
) {
ret.totalThisWeek = ret.totalThisWeek + ticket.productivehrs;
ret.employees[ticket.employee.employee_number].totalThisWeek =
ret.employees[ticket.employee.employee_number].totalThisWeek +
ticket.productivehrs;
} else if (
ticketDate.isBetween(
fixedPeriods.startOfLastWeek,
fixedPeriods.endOfLastWeek,
undefined,
"[]"
)
) {
ret.totalLastWeek = ret.totalLastWeek + ticket.productivehrs;
ret.employees[ticket.employee.employee_number].totalLastWeek =
ret.employees[ticket.employee.employee_number].totalLastWeek +
ticket.productivehrs;
}
if (
ticketDate.isBetween(
fixedPeriods.startofthisMonth,
fixedPeriods.endOfThisMonth,
undefined,
"[]"
)
) {
ret.totalThisMonth = ret.totalThisMonth + ticket.productivehrs;
ret.employees[ticket.employee.employee_number].totalThisMonth =
ret.employees[ticket.employee.employee_number].totalThisMonth +
ticket.productivehrs;
} else if (
ticketDate.isBetween(
fixedPeriods.startOfLastmonth,
fixedPeriods.endOfLastmonth,
undefined,
"[]"
)
) {
ret.totalLastMonth = ret.totalLastMonth + ticket.productivehrs;
ret.employees[ticket.employee.employee_number].totalLastMonth =
ret.employees[ticket.employee.employee_number].totalLastMonth +
ticket.productivehrs;
}
});
const ticketsGroupedByDate = _.groupBy(data.timetickets, "date");
const listOfDays = Utils.ListDaysBetween({
start: startDate,
end: endDate,
});
const employees = [];
const ret2 = [];
let totals = {
totalproductive: 0,
totalactual: 0,
employees: {},
};
listOfDays.forEach((day) => {
const r = {
date: moment(day).format("MM/DD"),
actualtotal: 0,
productivetotal: 0,
employees: {},
};
if (ticketsGroupedByDate[day]) {
ticketsGroupedByDate[day].forEach((ticket) => {
r.actualtotal = r.actualtotal + ticket.actualhrs;
r.productivetotal = r.productivetotal + ticket.productivehrs;
totals.totalactual = totals.totalactual + ticket.actualhrs;
totals.totalproductive =
totals.totalproductive + ticket.productivehrs;
employees.push(ticket.employee.employee_number);
//Add to table data.
ret.employees[ticket.employee.employee_number].totalOverPeriod =
ret.employees[ticket.employee.employee_number].totalOverPeriod +
ticket.productivehrs;
if (!totals.employees[ticket.employee.employee_number])
totals.employees[ticket.employee.employee_number] = {
totalactual: 0,
totalproductive: 0,
};
if (!r.employees[ticket.employee.employee_number])
r.employees[ticket.employee.employee_number] = {
actual: 0,
productive: 0,
};
//Add to totals.
totals.employees[ticket.employee.employee_number].totalproductive =
totals.employees[ticket.employee.employee_number].totalproductive +
ticket.productivehrs;
totals.employees[ticket.employee.employee_number].totalactual =
totals.employees[ticket.employee.employee_number].totalactual +
ticket.actualhrs;
//Add to dailys.
r.employees[ticket.employee.employee_number].productive =
r.employees[ticket.employee.employee_number].productive +
ticket.productivehrs;
r.employees[ticket.employee.employee_number].actual =
r.employees[ticket.employee.employee_number].actual +
ticket.actualhrs;
});
}
ret2.push(r);
});
return {
fixed: ret,
timeperiod: {
totals,
chartData: ret2,
employees: _.uniq(employees),
colors: getColorArray(employees.length),
},
};
}, [fixedPeriods, data, startDate, endDate]);
if (error) return <AlertComponent message={error.message} type="error" />;
if (loading) return <LoadingSpinner />;
return (
<div>
<TimeTicketsDatesSelector />
<ScoreboardTicketsBar
start={startDate}
end={endDate}
timetickets={data ? data.timetickets : []}
/>
</div>
<Row gutter={[16, 16]}>
<Col span={24}>
<ScoreboardTicketsStats data={calculatedData.fixed} />
</Col>
<Col span={24}>
<ScoreboardTicketsBar
start={startDate}
end={endDate}
data={calculatedData.timeperiod}
/>
</Col>
</Row>
);
}
@@ -43,3 +252,50 @@ export default function ScoreboardTimeTickets() {
//Hours produced in last 7 days
//Hours produced for time period by day
//Hours produced by employee by day for time period.
function getColorArray(num) {
return [
"#3366cc",
"#dc3912",
"#ff9900",
"#109618",
"#990099",
"#0099c6",
"#dd4477",
"#66aa00",
"#b82e2e",
"#316395",
"#3366cc",
"#994499",
"#22aa99",
"#aaaa11",
"#6633cc",
"#e67300",
"#8b0707",
"#651067",
"#329262",
"#5574a6",
"#3b3eac",
"#b77322",
"#16d620",
"#b91383",
"#f4359e",
"#9c5935",
"#a9c413",
"#2a778d",
"#668d1c",
"#bea413",
"#0c5922",
"#743411",
];
// var result = [];
// for (var i = 0; i < num; i += 1) {
// var letters = "0123456789ABCDEF".split("");
// var color = "#";
// for (var j = 0; j < 6; j += 1) {
// color += letters[Math.floor(Math.random() * 16)];
// }
// result.push(color);
// }
// return result;
}

View File

@@ -0,0 +1,101 @@
import { Card, Col, Row, Space, Statistic, Table } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(ScoreboardTicketsStats);
export function ScoreboardTicketsStats({ data, bodyshop }) {
const { t } = useTranslation();
const columns = [
{
title: t("employees.fields.employee_number"),
dataIndex: "employee_number",
key: "employee_number",
sorter: (a, b) => a.employee_number - b.employee_number,
},
{
title: t("scoreboard.labels.thisweek"),
dataIndex: "totalThisWeek",
key: "totalThisWeek",
sorter: (a, b) => a.totalThisWeek - b.totalThisWeek,
},
{
title: t("scoreboard.labels.lastweek"),
dataIndex: "totalLastWeek",
key: "totalLastWeek",
sorter: (a, b) => a.totalLastWeek - b.totalLastWeek,
},
{
title: t("scoreboard.labels.thismonth"),
dataIndex: "totalThisMonth",
key: "totalThisMonth",
sorter: (a, b) => a.totalThisMonth - b.totalThisMonth,
},
{
title: t("scoreboard.labels.lastmonth"),
dataIndex: "totalLastMonth",
key: "totalLastMonth",
sorter: (a, b) => a.totalLastMonth - b.totalLastMonth,
},
{
title: t("scoreboard.labels.totaloverperiod"),
dataIndex: "totalOverPeriod",
key: "totalOverPeriod",
sorter: (a, b) => a.totalOverPeriod - b.totalOverPeriod,
},
];
const tableData = data
? Object.keys(data.employees).map((key) => {
return { employee_number: key, ...data.employees[key] };
})
: [];
return (
<Card title={t("scoreboard.labels.productivestatistics")}>
<Row gutter={[16, 16]}>
<Col md={24} lg={6}>
<Space wrap>
<Statistic
title={t("scoreboard.labels.lastweek")}
value={data.totalLastWeek.toFixed(1)}
/>
<Statistic
title={t("scoreboard.labels.thisweek")}
value={data.totalThisWeek.toFixed(1)}
/>
<Statistic
title={t("scoreboard.labels.lastmonth")}
value={data.totalLastMonth.toFixed(1)}
/>
<Statistic
title={t("scoreboard.labels.thismonth")}
value={data.totalThisMonth.toFixed(1)}
/>
</Space>
</Col>
<Col md={24} lg={18}>
<Table
columns={columns}
dataSource={tableData}
id="employee_number"
/>
</Col>
</Row>
</Card>
);
}

View File

@@ -17,6 +17,7 @@ export default function TimeTicketsDatesSelector() {
if (!!start && !!end) {
history.push({
search: queryString.stringify({
...searchParams,
start: start.format("YYYY-MM-DD"),
end: end.format("YYYY-MM-DD"),
}),
@@ -25,6 +26,7 @@ export default function TimeTicketsDatesSelector() {
} else {
history.push({
search: queryString.stringify({
...searchParams,
start: null,
end: null,
}),