BOD-20 added onclick to production schedule + parts totals.

This commit is contained in:
Patrick Fic
2020-04-27 15:08:37 -07:00
parent 2b19d1af0b
commit f04a6fc2af
25 changed files with 580 additions and 120 deletions

View File

@@ -33,6 +33,19 @@ export function JobLinesComponent({
const { t } = useTranslation();
const columns = [
{
title: t("joblines.fields.line_no"),
dataIndex: "line_no",
key: "line_no",
// onFilter: (value, record) => record.ro_number.includes(value),
// filteredValue: state.filteredInfo.text || null,
sorter: (a, b) => a.line_no - b.line_no,
sortOrder:
state.sortedInfo.columnKey === "line_no" && state.sortedInfo.order,
//ellipsis: true,
editable: true,
width: 75,
},
{
title: t("joblines.fields.unq_seq"),
dataIndex: "unq_seq",
@@ -192,8 +205,7 @@ export function JobLinesComponent({
actions: { refetch: refetch },
context: record,
});
}}
>
}}>
{t("general.actions.edit")}
</Button>
</span>
@@ -229,8 +241,7 @@ export function JobLinesComponent({
linesToOrder: selectedLines,
},
});
}}
>
}}>
{t("parts.actions.order")}
</Button>
<AllocationsBulkAssignmentContainer
@@ -243,15 +254,14 @@ export function JobLinesComponent({
actions: { refetch: refetch },
context: { jobid: jobId },
});
}}
>
}}>
{t("joblines.actions.new")}
</Button>
</div>
);
}}
loading={loading}
size="small"
size='small'
expandedRowRender={(record) => (
<div style={{ margin: 0 }}>
<strong>{t("parts_orders.labels.orderhistory")}</strong>
@@ -276,7 +286,7 @@ export function JobLinesComponent({
setSelectedLines(selectedRows),
}}
columns={columns.map((item) => ({ ...item }))}
rowKey="id"
rowKey='id'
dataSource={jobLines}
onChange={handleTableChange}
/>

View File

@@ -18,7 +18,7 @@ const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export function JobsDetailHeaderActions({ job, bodyshop }) {
export function JobsDetailHeaderActions({ job, bodyshop, refetch }) {
const { t } = useTranslation();
const client = useApolloClient();
const history = useHistory();
@@ -36,7 +36,7 @@ export function JobsDetailHeaderActions({ job, bodyshop }) {
<Menu.Item
key='addtoproduction'
disabled={!!!job.converted || !!job.inproduction}
onClick={() => AddToProduction(client, job.id)}>
onClick={() => AddToProduction(client, job.id, refetch)}>
{t("jobs.actions.addtoproduction")}
</Menu.Item>
<Menu.Item key='duplicatejob'>

View File

@@ -101,7 +101,7 @@ export function JobsDetailHeader({
}}>
{t("jobs.actions.convert")}
</Button>,
<JobsDetailHeaderActions key='actions' job={job} />,
<JobsDetailHeaderActions key='actions' job={job} refetch={refetch} />,
<Button type='primary' key='submit' htmlType='submit'>
{t("general.actions.save")}
</Button>,

View File

@@ -0,0 +1,43 @@
import React, { useState } from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { Pie } from "@nivo/pie";
import { useTranslation } from "react-i18next";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
export function PartsStatusPie({ partsList }) {
const { t } = useTranslation();
//const [pieData, setPieData] = useState([]);
const result = partsList
? partsList.reduce((names, name) => {
const val = name || "?";
const count = names[val] || 0;
names[val] = count + 1;
return names;
}, {})
: {};
const pieData = Object.keys(result).map((i) => {
console.log("i", i);
return {
id: i,
label: i,
value: result[i],
};
});
const commonProperties = {
width: 250,
height: 250,
margin: { top: 40, right: 60, bottom: 40, left: 60 },
animate: true,
};
return <Pie {...commonProperties} data={pieData} innerRadius={0.5} />;
}
export default connect(mapStateToProps, null)(PartsStatusPie);

View File

@@ -0,0 +1,58 @@
import React from "react";
import { Descriptions, Drawer } from "antd";
import { useTranslation } from "react-i18next";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { DateFormatter } from "../../utils/DateFormatter";
import PartsPieGraph from "../parts-status-pie/parts-status-pie.component";
import Barcode from "react-barcode";
export default function ProductionListDetail({ selected, setSelected, jobs }) {
const { t } = useTranslation();
const theJob = jobs.find((j) => j.id === selected) || {};
return (
<Drawer
title={t("production.labels.jobdetail")}
placement='right'
width={"25%"}
onClose={() => setSelected(null)}
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 || ""}
</Descriptions.Item>
<Descriptions.Item label={t("jobs.fields.owner")}>
{`${theJob.ownr_fn || ""} ${theJob.ownr_ln || ""} ${
theJob.ownr_co_nm || ""
}`}
</Descriptions.Item>
<Descriptions.Item label={t("jobs.fields.vehicle")}>
{`${theJob.v_model_yr || ""} ${theJob.v_color || ""} ${
theJob.v_make_desc || ""
} ${theJob.v_model_desc || ""}`}
</Descriptions.Item>
<Descriptions.Item label={t("jobs.fields.clm_total")}>
<CurrencyFormatter>{theJob.clm_total}</CurrencyFormatter>
</Descriptions.Item>
<Descriptions.Item label={t("jobs.fields.actual_in")}>
<DateFormatter>{theJob.actual_in}</DateFormatter>
</Descriptions.Item>
<Descriptions.Item label={t("jobs.fields.scheduled_completion")}>
<DateFormatter>{theJob.scheduled_completion}</DateFormatter>
</Descriptions.Item>
<Descriptions.Item label={t("jobs.labels.parts")}>
<PartsPieGraph partsList={theJob.partcount} />
</Descriptions.Item>
</Descriptions>
</div>
</Drawer>
);
}

View File

@@ -1,23 +1,20 @@
import { Table, Button, Menu, Dropdown, Input } from "antd";
import React, { useState } from "react";
import { SyncOutlined } from "@ant-design/icons";
import ProductionListSaveConfigButton from "../production-list-save-config-button/production-list-save-config-button.component";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { Button, Dropdown, Input, Menu, Table } from "antd";
import React, { useState } from "react";
import ReactDragListView from "react-drag-listview"; //TODO Is there a better way? This library is too big.
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import ReactDragListView from "react-drag-listview"; //TODO Is there a better way? This library is too big.
import "./production-list-table.styles.scss";
import { useTranslation } from "react-i18next";
import { selectBodyshop } from "../../redux/user/user.selectors";
import ProductionListColumnsAdd from "../production-list-columns/production-list-columns.add.component";
import ProductionListSaveConfigButton from "../production-list-save-config-button/production-list-save-config-button.component";
import ResizeableTitle from "./production-list-table.resizeable.component";
import "./production-list-table.styles.scss";
import ProductionListDetail from "../production-list-detail/production-list-detail.component";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
const OneCalendarDay = 60 * 60 * 24 * 1000;
@@ -36,10 +33,10 @@ export function ProductionListTable({
filteredInfo: { text: "" },
}
);
const [selected, setSelected] = useState(null);
const { t } = useTranslation();
const Now = new Date();
const handleTableChange = (pagination, filters, sorter) => {
console.log("sorter", sorter);
setState({
...state,
filteredInfo: filters,
@@ -68,8 +65,6 @@ export function ProductionListTable({
setColumns(nextColumns);
};
const Now = new Date();
const headerItem = (col) => (
<Dropdown
className='prod-header-dropdown'
@@ -84,102 +79,117 @@ export function ProductionListTable({
<span>{col.title}</span>
</Dropdown>
);
const dataSource =
searchText === ""
? data
: data.filter(
(j) =>
(j.ro_number || "")
.toString()
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.ownr_fn || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.ownr_ln || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.status || "").toLowerCase().includes(searchText.toLowerCase()) ||
(j.ins_co_nm || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.clm_no || "").toLowerCase().includes(searchText.toLowerCase()) ||
(j.v_model_desc || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.v_make_desc || "")
.toLowerCase()
.includes(searchText.toLowerCase())
);
const tableTitle = () => (
<div style={{ display: "flex" }}>
<ProductionListColumnsAdd columnState={columnState} />
<ProductionListSaveConfigButton columns={columns} tableState={state} />
<Button
onClick={() => {
if (refetch) refetch();
}}>
<SyncOutlined />
</Button>
<Input
onChange={(e) => setSearchText(e.target.value)}
placeholder={t("general.labels.search")}
value={searchText}
/>
</div>
);
const handleSelectRecord = (record) => {
if (selected !== record.id) {
setSelected(record.id);
} else {
setSelected(null);
}
};
if (!!!columns) return <div>No columns found.</div>;
return (
<ReactDragListView.DragColumn
onDragEnd={onDragEnd}
nodeSelector='th'
handleSelector='.prod-header-dropdown'>
<Table
size='small'
pagination={false}
components={{
header: {
cell: ResizeableTitle,
},
}}
title={() => (
<div style={{ display: "flex" }}>
<ProductionListColumnsAdd columnState={columnState} />
<ProductionListSaveConfigButton
columns={columns}
tableState={state}
/>
<Button
onClick={() => {
if (refetch) refetch();
}}>
<SyncOutlined />
</Button>
<Input
onChange={(e) => setSearchText(e.target.value)}
placeholder={t("general.labels.search")}
value={searchText}
/>
</div>
)}
columns={columns.map((c, index) => {
return {
...c,
sortOrder:
state.sortedInfo.columnKey === c.key && state.sortedInfo.order,
title: headerItem(c),
onHeaderCell: (column) => ({
width: column.width,
onResize: handleResize(index),
}),
};
})}
rowKey='id'
loading={loading}
dataSource={
searchText === ""
? data
: data.filter(
(j) =>
(j.ro_number || "")
.toString()
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.ownr_fn || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.ownr_ln || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.status || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.ins_co_nm || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.clm_no || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.v_model_desc || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.v_make_desc || "")
.toLowerCase()
.includes(searchText.toLowerCase())
)
}
onChange={handleTableChange}
rowClassName={(record, index) => {
const classes = [];
if (!!record.scheduled_completion) {
if (new Date(record.scheduled_completion) - Now < OneCalendarDay)
classes.push("production-completion-1");
}
return classes.join(" ");
}} //TODO What could be good usage here?
onRow={(record, index) => (record.refetch = refetch)}
<div>
<ProductionListDetail
selected={selected}
setSelected={setSelected}
jobs={dataSource}
/>
</ReactDragListView.DragColumn>
<ReactDragListView.DragColumn
onDragEnd={onDragEnd}
nodeSelector='th'
handleSelector='.prod-header-dropdown'>
<Table
size='small'
pagination={false}
components={{
header: {
cell: ResizeableTitle,
},
}}
title={tableTitle}
columns={columns.map((c, index) => {
return {
...c,
sortOrder:
state.sortedInfo.columnKey === c.key && state.sortedInfo.order,
title: headerItem(c),
onHeaderCell: (column) => ({
width: column.width,
onResize: handleResize(index),
}),
};
})}
rowKey='id'
loading={loading}
onRow={(record, rowIndex) => {
return {
onClick: (event) => {
handleSelectRecord(record);
},
};
}}
dataSource={dataSource}
onChange={handleTableChange}
rowClassName={(record, index) => {
const classes = []; //TODO What could be good usage here?
if (!!record.scheduled_completion) {
if (new Date(record.scheduled_completion) - Now < OneCalendarDay)
classes.push("production-completion-1");
}
return classes.join(" ");
}}
/>
</ReactDragListView.DragColumn>
</div>
);
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(ProductionListTable);
export default connect(mapStateToProps, null)(ProductionListTable);

View File

@@ -104,6 +104,7 @@ export const SUBSCRIPTION_JOBS_IN_PRODUCTION = gql`
production_vars
labhrs
larhrs
partcount
}
}
`;
@@ -248,7 +249,7 @@ export const GET_JOB_BY_PK = gql`
date_exported
status
owner_owing
joblines {
id
unq_seq

View File

@@ -739,6 +739,7 @@
"bodyhours": "B",
"bodypriority": "B/P",
"cycletime": "C/T",
"jobdetail": "Job Details",
"note": "Production Note",
"paintpriority": "P/P",
"refinishhours": "R"

View File

@@ -739,6 +739,7 @@
"bodyhours": "",
"bodypriority": "",
"cycletime": "",
"jobdetail": "",
"note": "",
"paintpriority": "",
"refinishhours": ""

View File

@@ -739,6 +739,7 @@
"bodyhours": "",
"bodypriority": "",
"cycletime": "",
"jobdetail": "",
"note": "",
"paintpriority": "",
"refinishhours": ""