215 lines
6.7 KiB
JavaScript
215 lines
6.7 KiB
JavaScript
import React from "react";
|
|
import { Statistic, Space, List, Button, Typography } from "antd";
|
|
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
|
import { useTranslation } from "react-i18next";
|
|
import moment from "moment";
|
|
import RenderTemplate, {
|
|
displayTemplateInWindow,
|
|
} from "../../utils/RenderTemplate";
|
|
import { TemplateList } from "../../utils/TemplateConstants";
|
|
import { connect } from "react-redux";
|
|
import { createStructuredSelector } from "reselect";
|
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
|
import { onlyUnique } from "../../utils/arrayHelper";
|
|
|
|
const mapStateToProps = createStructuredSelector({
|
|
bodyshop: selectBodyshop,
|
|
});
|
|
const mapDispatchToProps = (dispatch) => ({
|
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
|
});
|
|
|
|
export function TimeTicketsSummaryEmployees({
|
|
bodyshop,
|
|
loading,
|
|
timetickets,
|
|
startDate,
|
|
endDate,
|
|
}) {
|
|
const { t } = useTranslation();
|
|
|
|
//Group everything by employee
|
|
//Then sum the individual time TimeTicketsSummary.
|
|
|
|
//Calculate job based tickets.
|
|
const jobTicketsByEmployee = {};
|
|
timetickets
|
|
.filter((i) => i.cost_center !== "timetickets.labels.shift")
|
|
.map((tt) => {
|
|
if (!!!jobTicketsByEmployee[tt.employeeid]) {
|
|
jobTicketsByEmployee[tt.employeeid] = [];
|
|
}
|
|
jobTicketsByEmployee[tt.employeeid].push(tt);
|
|
return null;
|
|
});
|
|
const jobTickets = Object.keys(jobTicketsByEmployee).map(function (key) {
|
|
return {
|
|
employee: jobTicketsByEmployee[key][0].employee,
|
|
tickets: jobTicketsByEmployee[key],
|
|
};
|
|
});
|
|
|
|
//Calculate shift based tickets.
|
|
const shiftTicketsByEmployee = {};
|
|
timetickets
|
|
.filter((i) => i.cost_center === "timetickets.labels.shift")
|
|
.map((tt) => {
|
|
if (!!!shiftTicketsByEmployee[tt.employeeid]) {
|
|
shiftTicketsByEmployee[tt.employeeid] = [];
|
|
}
|
|
shiftTicketsByEmployee[tt.employeeid].push(tt);
|
|
return null;
|
|
});
|
|
const shiftTickets = Object.keys(shiftTicketsByEmployee).map(function (key) {
|
|
return {
|
|
employee: shiftTicketsByEmployee[key][0].employee,
|
|
tickets: shiftTicketsByEmployee[key],
|
|
};
|
|
});
|
|
|
|
const handlePrintEmployeeTicket = async (empId) => {
|
|
const html = await RenderTemplate(
|
|
{
|
|
name: TemplateList().time_tickets_by_employee.key,
|
|
variables: { id: empId, start: startDate, end: endDate },
|
|
},
|
|
bodyshop
|
|
);
|
|
displayTemplateInWindow(html);
|
|
};
|
|
|
|
return (
|
|
<div>
|
|
<List
|
|
header={
|
|
<Typography.Title level={3}>
|
|
{t("timetickets.labels.jobhours")}
|
|
</Typography.Title>
|
|
}
|
|
itemLayout="horizontal"
|
|
//dataSource={jobTickets}
|
|
>
|
|
{jobTickets.map((item, idx) => {
|
|
const employeeCostCenters = item.tickets
|
|
.map((i) => i.cost_center)
|
|
.filter(onlyUnique);
|
|
|
|
return employeeCostCenters.map((costCenter) => {
|
|
const actHrs = item.tickets
|
|
.filter((ticket) => ticket.cost_center === costCenter)
|
|
.reduce((acc, val) => acc + val.actualhrs, 0);
|
|
|
|
const prodHrs = item.tickets
|
|
.filter((ticket) => ticket.cost_center === costCenter)
|
|
.reduce((acc, val) => acc + val.productivehrs, 0);
|
|
|
|
const clockHrs = item.tickets
|
|
.filter((ticket) => ticket.cost_center === costCenter)
|
|
.reduce((acc, val) => {
|
|
if (!!val.clockoff && !!val.clockon)
|
|
return (
|
|
acc +
|
|
moment(val.clockoff).diff(
|
|
moment(val.clockon),
|
|
"hours",
|
|
true
|
|
)
|
|
);
|
|
return acc;
|
|
}, 0);
|
|
|
|
return (
|
|
<List.Item
|
|
key={`${idx}${costCenter}`}
|
|
actions={[
|
|
<Button
|
|
onClick={() => handlePrintEmployeeTicket(item.employee.id)}
|
|
>
|
|
{t("timetickets.actions.printemployee")}
|
|
</Button>,
|
|
]}
|
|
>
|
|
<LoadingSkeleton loading={loading}>
|
|
<List.Item.Meta
|
|
title={`${item.employee.first_name} ${item.employee.last_name}`}
|
|
description={costCenter}
|
|
/>
|
|
<Space>
|
|
<Statistic
|
|
title={t("timetickets.fields.actualhrs")}
|
|
precision={1}
|
|
value={actHrs}
|
|
/>
|
|
<Statistic
|
|
title={t("timetickets.fields.productivehrs")}
|
|
precision={1}
|
|
value={prodHrs}
|
|
/>
|
|
<Statistic
|
|
title={t("timetickets.fields.efficiency")}
|
|
precision={1}
|
|
value={(prodHrs / actHrs) * 100}
|
|
suffix={"%"}
|
|
/>
|
|
<Statistic
|
|
title={t("timetickets.fields.clockhours")}
|
|
precision={1}
|
|
value={clockHrs}
|
|
/>
|
|
</Space>
|
|
</LoadingSkeleton>
|
|
</List.Item>
|
|
);
|
|
});
|
|
})}
|
|
</List>
|
|
<List
|
|
header={
|
|
<Typography.Title level={3}>
|
|
{t("timetickets.labels.clockhours")}
|
|
</Typography.Title>
|
|
}
|
|
itemLayout="horizontal"
|
|
dataSource={shiftTickets}
|
|
renderItem={(item) => {
|
|
const clockHrs = item.tickets.reduce((acc, val) => {
|
|
if (!!val.clockoff && !!val.clockon)
|
|
return (
|
|
acc +
|
|
moment(val.clockoff).diff(moment(val.clockon), "hours", true)
|
|
);
|
|
return acc;
|
|
}, 0);
|
|
|
|
return (
|
|
<List.Item
|
|
actions={[
|
|
<Button
|
|
onClick={() => handlePrintEmployeeTicket(item.employee.id)}
|
|
>
|
|
{t("timetickets.actions.printemployee")}
|
|
</Button>,
|
|
]}
|
|
>
|
|
<LoadingSkeleton loading={loading}>
|
|
<List.Item.Meta
|
|
title={`${item.employee.first_name} ${item.employee.last_name}`}
|
|
/>
|
|
<Statistic
|
|
title={t("timetickets.fields.clockhours")}
|
|
precision={2}
|
|
value={clockHrs}
|
|
/>
|
|
</LoadingSkeleton>
|
|
</List.Item>
|
|
);
|
|
}}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|
|
export default connect(
|
|
mapStateToProps,
|
|
mapDispatchToProps
|
|
)(TimeTicketsSummaryEmployees);
|