Added sublets tracking to production board. BOD-415
This commit is contained in:
@@ -6,7 +6,6 @@ import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { Link, useLocation } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import { DateFormatter } from "../../utils/DateFormatter";
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Link } from "react-router-dom";
|
||||
import "./production-board-card.styles.scss";
|
||||
import ProductionRemoveButton from "../production-remove-button/production-remove-button.component";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import ProductionSubletsManageComponent from "../production-sublets-manage/production-sublets-manage.component";
|
||||
|
||||
export default function ProductionBoardCard(card) {
|
||||
const { t } = useTranslation();
|
||||
@@ -77,8 +78,9 @@ export default function ProductionBoardCard(card) {
|
||||
</Row>
|
||||
<div className="imex-flex-row imex-flex-row__flex-space-around">
|
||||
<ProductionAlert record={card} key="alert" />
|
||||
<ProductionSubletsManageComponent subletJobLines={card.subletLines} />
|
||||
<Link to={`/manage/jobs/${card.id}`}>
|
||||
<EyeFilled key="setting" />
|
||||
<EyeFilled />
|
||||
</Link>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
@@ -5,12 +5,13 @@ import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import { DateFormatter } from "../../utils/DateFormatter";
|
||||
import PhoneFormatter from "../../utils/PhoneFormatter";
|
||||
import { alphaSort } from "../../utils/sorters";
|
||||
import ProductionSubletsManageComponent from "../production-sublets-manage/production-sublets-manage.component";
|
||||
import ProductionListColumnAlert from "./production-list-columns.alert.component";
|
||||
import ProductionListColumnBodyPriority from "./production-list-columns.bodypriority.component";
|
||||
import ProductionListColumnPaintPriority from "./production-list-columns.paintpriority.component";
|
||||
import ProductionListColumnStatus from "./production-list-columns.status.component";
|
||||
import ProductionListColumnNote from "./production-list-columns.productionnote.component";
|
||||
|
||||
import ProductionListColumnStatus from "./production-list-columns.status.component";
|
||||
import ProductionlistColumnTouchTime from "./prodution-list-columns.touchtime.component";
|
||||
|
||||
export default [
|
||||
{
|
||||
@@ -19,7 +20,9 @@ export default [
|
||||
key: "viewdetail",
|
||||
ellipsis: true,
|
||||
render: (text, record) => (
|
||||
<Link to={{search:`?selected=${record.id}`}} >{i18n.t("general.labels.view")}</Link>
|
||||
<Link to={{ search: `?selected=${record.id}` }}>
|
||||
{i18n.t("general.labels.view")}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -179,16 +182,8 @@ export default [
|
||||
title: i18n.t("production.labels.cycletime"),
|
||||
dataIndex: "ct",
|
||||
key: "ct",
|
||||
|
||||
render: (text, record) => {
|
||||
let ct = 0;
|
||||
if (!!record.actual_in) {
|
||||
const totalHrs = record.larhrs + record.labhrs;
|
||||
const Difference_In_Time = new Date() - new Date(record.actual_in);
|
||||
const Difference_In_Days = Difference_In_Time / (1000 * 3600 * 24);
|
||||
ct = (totalHrs / Difference_In_Days).toFixed(2);
|
||||
}
|
||||
return <span>{ct || 0}</span>;
|
||||
return <ProductionlistColumnTouchTime job={record} />;
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -215,4 +210,12 @@ export default [
|
||||
<ProductionListColumnPaintPriority record={record} />
|
||||
),
|
||||
},
|
||||
{
|
||||
title: i18n.t("production.labels.sublets"),
|
||||
dataIndex: "sublets",
|
||||
key: "sublets",
|
||||
render: (text, record) => (
|
||||
<ProductionSubletsManageComponent subletJobLines={record.subletLines} />
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import moment from "moment";
|
||||
import React, { useMemo } from "react";
|
||||
export default function ProductionlistColumnTouchTime({ job }) {
|
||||
let ct = useMemo(() => {
|
||||
if (!!job.actual_in) {
|
||||
const totalHrs =
|
||||
(job.larhrs.aggregate.sum.mod_lb_hrs || 0) +
|
||||
(job.labhrs.aggregate.sum.mod_lb_hrs || 0);
|
||||
|
||||
const Difference_In_Days = moment().diff(
|
||||
moment(job.actual_in),
|
||||
"days",
|
||||
true
|
||||
);
|
||||
|
||||
return (totalHrs / Difference_In_Days).toFixed(2);
|
||||
}
|
||||
return 0;
|
||||
}, [job]);
|
||||
|
||||
return <span>{ct}</span>;
|
||||
}
|
||||
@@ -1,13 +1,12 @@
|
||||
import React from "react";
|
||||
import { Descriptions, Drawer } from "antd";
|
||||
import queryString from "query-string";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useHistory, useLocation } from "react-router-dom";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import { DateFormatter } from "../../utils/DateFormatter";
|
||||
import PartsStatusPie from "../parts-status-pie/parts-status-pie.component";
|
||||
import Barcode from "react-barcode";
|
||||
import ProductionRemoveButton from "../production-remove-button/production-remove-button.component";
|
||||
import queryString from "query-string";
|
||||
import { useHistory, useLocation } from "react-router-dom";
|
||||
|
||||
export default function ProductionListDetail({ jobs }) {
|
||||
const search = queryString.parse(useLocation().search);
|
||||
@@ -31,13 +30,6 @@ export default function ProductionListDetail({ jobs }) {
|
||||
visible={!!selected}
|
||||
>
|
||||
<div>
|
||||
<Barcode
|
||||
value={theJob.id || ""}
|
||||
background="transparent"
|
||||
displayValue={false}
|
||||
width={1}
|
||||
height={15}
|
||||
/>
|
||||
<Descriptions bordered size="small" column={1}>
|
||||
<Descriptions.Item label={t("jobs.fields.ro_number")}>
|
||||
{theJob.ro_number || ""}
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
import { CheckCircleFilled, EyeInvisibleFilled } from "@ant-design/icons";
|
||||
import { Button, List, notification, Popover } from "antd";
|
||||
import React, { useMemo, useState } from "react";
|
||||
import { useMutation } from "react-apollo";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { UPDATE_JOB_LINE } from "../../graphql/jobs-lines.queries";
|
||||
export default function ProductionSubletsManageComponent({ subletJobLines }) {
|
||||
const { t } = useTranslation();
|
||||
const [updateJobLine] = useMutation(UPDATE_JOB_LINE);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const subletCount = useMemo(() => {
|
||||
return {
|
||||
total: subletJobLines.filter((s) => !s.sublet_ignored).length,
|
||||
outstanding: subletJobLines.filter(
|
||||
(s) => !s.sublet_ignored && !s.sublet_completed
|
||||
).length,
|
||||
};
|
||||
}, [subletJobLines]);
|
||||
|
||||
const handleSubletMark = async (sublet, action) => {
|
||||
setLoading(true);
|
||||
|
||||
const result = await updateJobLine({
|
||||
variables: {
|
||||
lineId: sublet.id,
|
||||
line: {
|
||||
sublet_completed:
|
||||
action === "complete" ? !sublet.sublet_completed : false,
|
||||
sublet_ignored: action === "ignore" ? !sublet.sublet_ignored : false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!!result.errors) {
|
||||
notification["error"]({
|
||||
message: t("joblines.errors.updating", {
|
||||
message: JSON.stringify(result.errors),
|
||||
}),
|
||||
});
|
||||
} else {
|
||||
notification["success"]({
|
||||
message: t("joblines.successes.updated"),
|
||||
});
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
const popContent = (
|
||||
<div style={{ minWidth: "20rem" }}>
|
||||
<List
|
||||
size="small"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
dataSource={subletJobLines}
|
||||
renderItem={(s) => (
|
||||
<List.Item
|
||||
actions={[
|
||||
<Button
|
||||
key="complete"
|
||||
loading={loading}
|
||||
onClick={() => handleSubletMark(s, "complete")}
|
||||
type={s.sublet_completed ? "primary" : "ghost"}
|
||||
>
|
||||
<CheckCircleFilled
|
||||
color={s.sublet_completed ? "green" : null}
|
||||
/>
|
||||
</Button>,
|
||||
<Button
|
||||
key="sublet"
|
||||
loading={loading}
|
||||
onClick={() => handleSubletMark(s, "ignore")}
|
||||
type={s.sublet_ignored ? "primary" : "ghost"}
|
||||
>
|
||||
<EyeInvisibleFilled
|
||||
color={s.sublet_ignored ? "tomato" : null}
|
||||
/>
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
<List.Item.Meta title={s.line_desc} />
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Popover
|
||||
trigger={["click"]}
|
||||
content={popContent}
|
||||
style={{ cursor: "pointer" }}
|
||||
placement="bottom"
|
||||
title={t("production.labels.sublets")}
|
||||
>
|
||||
<span>{`${subletCount.outstanding} / ${subletCount.total} rem.`}</span>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user