WIP Time Ticket Summary BOD-191

This commit is contained in:
Patrick Fic
2020-07-20 08:55:01 -07:00
parent a54a85b96c
commit f187a2106c
14 changed files with 544 additions and 18 deletions

View File

@@ -0,0 +1,33 @@
import { DatePicker } from "antd";
import moment from "moment";
import queryString from "query-string";
import React from "react";
import { useHistory, useLocation } from "react-router-dom";
export default function TimeTicketsDatesSelector() {
const searchParams = queryString.parse(useLocation().search);
const { start, end } = searchParams;
const history = useHistory();
const handleChange = (dates) => {
const [start, end] = dates;
if (!!start && !!end) {
history.push({
search: queryString.stringify({
start: start.format("YYYY-MM-DD"),
end: end.format("YYYY-MM-DD"),
}),
});
}
};
return (
<div>
<DatePicker.RangePicker
defaultValue={[moment(start), moment(end)]}
onCalendarChange={handleChange}
/>
</div>
);
}

View File

@@ -5,6 +5,9 @@ import { Link } from "react-router-dom";
import { alphaSort } from "../../utils/sorters";
import { DateFormatter } from "../../utils/DateFormatter";
import TimeTicketEnterButton from "../time-ticket-enter-button/time-ticket-enter-button.component";
import { DateTimeFormatter } from "../../utils/DateFormatter";
import moment from "moment";
import { onlyUnique } from "../../utils/arrayHelper";
export default function TimeTicketList({
loading,
@@ -37,8 +40,7 @@ export default function TimeTicketList({
sortOrder: state.sortedInfo.columnKey === "vin" && state.sortedInfo.order,
render: (text, record) => (
<Link
to={`/manage/employees/${record.employee.id}`}
>{`${record.employee.first_name} ${record.employee.last_name}`}</Link>
to={`/manage/employees/${record.employee.id}`}>{`${record.employee.first_name} ${record.employee.last_name}`}</Link>
),
},
{
@@ -48,6 +50,17 @@ export default function TimeTicketList({
sorter: (a, b) => alphaSort(a.cost_center, b.cost_center),
sortOrder:
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
filters:
timetickets
.map((l) => l.cost_center)
.filter(onlyUnique)
.map((s) => {
return {
text: s, //|| "No Status*",
value: [s],
};
}) || [],
onFilter: (value, record) => value.includes(record.cost_center),
},
{
title: t("timetickets.fields.productivehrs"),
@@ -66,6 +79,49 @@ export default function TimeTicketList({
sortOrder:
state.sortedInfo.columnKey === "actualhrs" && state.sortedInfo.order,
},
{
title: t("timetickets.fields.clockon"),
dataIndex: "clockon",
key: "clockon",
sorter: (a, b) => a.clockon - b.clockon,
sortOrder:
state.sortedInfo.columnKey === "clockon" && state.sortedInfo.order,
render: (text, record) => (
<DateTimeFormatter>{record.clockon}</DateTimeFormatter>
),
},
{
title: t("timetickets.fields.clockoff"),
dataIndex: "clockoff",
key: "clockoff",
sorter: (a, b) => a.clockoff - b.clockoff,
sortOrder:
state.sortedInfo.columnKey === "clockoff" && state.sortedInfo.order,
render: (text, record) => (
<DateTimeFormatter>{record.clockoff}</DateTimeFormatter>
),
},
{
title: t("timetickets.fields.clockhours"),
dataIndex: "clockoff",
key: "clockoff",
sorter: (a, b) => a.clockoff - b.clockoff,
sortOrder:
state.sortedInfo.columnKey === "clockoff" && state.sortedInfo.order,
render: (text, record) => {
if (record.clockoff && record.clockon)
return (
<div>
{moment(record.clockoff)
.diff(moment(record.clockon), "hours", true)
.toFixed(2)}
</div>
);
else {
return null;
}
},
},
{
title: t("general.labels.actions"),
dataIndex: "actions",
@@ -75,8 +131,7 @@ export default function TimeTicketList({
return (
<TimeTicketEnterButton
actions={{ refetch }}
context={{ id: record.id, timeticket: record }}
>
context={{ id: record.id, timeticket: record }}>
{t("general.actions.edit")}
</TimeTicketEnterButton>
);
@@ -91,10 +146,10 @@ export default function TimeTicketList({
return (
<Table
loading={loading}
size="small"
size='small'
pagination={{ position: "top" }}
columns={columns.map((item) => ({ ...item }))}
rowKey="id"
rowKey='id'
dataSource={timetickets}
onChange={handleTableChange}
/>

View File

@@ -59,7 +59,7 @@ export function TimeTicketModalContainer({
} else {
insertTicket({
variables: {
timeTicketInput: [{ ...values, bodyshop: bodyshop.id }],
timeTicketInput: [{ ...values, bodyshopid: bodyshop.id }],
},
})
.then(handleMutationSuccess)

View File

@@ -0,0 +1,131 @@
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";
export default function TimeTicketsSummaryEmployees({ loading, timetickets }) {
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);
});
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);
});
const shiftTickets = Object.keys(shiftTicketsByEmployee).map(function (key) {
return {
employee: shiftTicketsByEmployee[key][0].employee,
tickets: shiftTicketsByEmployee[key],
};
});
return (
<div>
<List
header={
<Typography.Title level={3}>
{t("timetickets.labels.jobhours")}
</Typography.Title>
}
itemLayout='horizontal'
dataSource={jobTickets}
renderItem={(item) => (
<List.Item
actions={[
<Button>{t("timetickets.actions.printemployee")}</Button>,
]}>
<LoadingSkeleton loading={loading}>
<List.Item.Meta
title={
<a href='https://ant.design'>{`${item.employee.first_name} ${item.employee.last_name}`}</a>
}
// description='Ant Design, a design language for background applications, is refined by Ant UED Team'
/>
<Space>
<Statistic
title={t("timetickets.fields.actualhrs")}
precision={1}
value={item.tickets.reduce(
(acc, val) => acc + val.actualhrs,
0
)}
/>
<Statistic
title={t("timetickets.fields.productivehrs")}
precision={1}
value={item.tickets.reduce(
(acc, val) => acc + val.productivehrs,
0
)}
/>
</Space>
</LoadingSkeleton>
</List.Item>
)}
/>
<List
header={
<Typography.Title level={3}>
{t("timetickets.labels.clockhours")}
</Typography.Title>
}
itemLayout='horizontal'
dataSource={shiftTickets}
renderItem={(item) => (
<List.Item
actions={[
<Button>{t("timetickets.actions.printemployee")}</Button>,
]}>
<LoadingSkeleton loading={loading}>
<List.Item.Meta
title={
<a href='https://ant.design'>{`${item.employee.first_name} ${item.employee.last_name}`}</a>
}
// description='Ant Design, a design language for background applications, is refined by Ant UED Team'
/>
<Statistic
title={t("timetickets.fields.clockhours")}
precision={2}
value={item.tickets.reduce(
(acc, val) =>
acc +
moment(item.clockoff).diff(
moment(item.clockon),
"hours",
true
),
0
)}
/>
</LoadingSkeleton>
</List.Item>
)}
/>
</div>
);
}

View File

@@ -0,0 +1,14 @@
import React from "react";
import TimeTicketsSummaryEmployees from "../time-tickets-summary-employees/time-tickets-summary-employees.component";
export default function TimeTicketsSummary({ loading, timetickets }) {
console.log("ordera ds");
return (
<div>
<TimeTicketsSummaryEmployees
loading={loading}
timetickets={timetickets}
/>
</div>
);
}