Allow for clear and correct for Select allowClear sending undefined and convert that to null. Overload UndefinedtoNull to have an array of keys
392 lines
12 KiB
JavaScript
392 lines
12 KiB
JavaScript
import Icon, {
|
|
BarsOutlined,
|
|
CalendarFilled,
|
|
DollarCircleOutlined,
|
|
FileImageFilled,
|
|
PrinterFilled,
|
|
ToolFilled,
|
|
HistoryOutlined,
|
|
SyncOutlined,
|
|
} from "@ant-design/icons";
|
|
import {
|
|
Button,
|
|
Divider,
|
|
Form,
|
|
notification,
|
|
PageHeader,
|
|
Space,
|
|
Tabs,
|
|
} from "antd";
|
|
import Axios from "axios";
|
|
import moment from "moment";
|
|
import queryString from "query-string";
|
|
import React, { useEffect, useState } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import { FaHardHat, FaRegStickyNote, FaShieldAlt } from "react-icons/fa";
|
|
import { connect } from "react-redux";
|
|
import { useHistory, useLocation } from "react-router-dom";
|
|
import { createStructuredSelector } from "reselect";
|
|
import FormFieldsChanged from "../../components/form-fields-changed-alert/form-fields-changed-alert.component";
|
|
import JobsLinesContainer from "../../components/job-detail-lines/job-lines.container";
|
|
import JobLineUpsertModalContainer from "../../components/job-lines-upsert-modal/job-lines-upsert-modal.container";
|
|
import JobReconciliationModal from "../../components/job-reconciliation-modal/job-reconciliation.modal.container";
|
|
import JobSyncButton from "../../components/job-sync-button/job-sync-button.component";
|
|
import JobsChangeStatus from "../../components/jobs-change-status/jobs-change-status.component";
|
|
import JobsConvertButton from "../../components/jobs-convert-button/jobs-convert-button.component";
|
|
import JobsDetailDatesComponent from "../../components/jobs-detail-dates/jobs-detail-dates.component";
|
|
import JobsDetailGeneral from "../../components/jobs-detail-general/jobs-detail-general.component";
|
|
import JobsDetailHeaderActions from "../../components/jobs-detail-header-actions/jobs-detail-header-actions.component";
|
|
import JobsDetailHeader from "../../components/jobs-detail-header/jobs-detail-header.component";
|
|
import JobsDetailLaborContainer from "../../components/jobs-detail-labor/jobs-detail-labor.container";
|
|
import JobsDetailPliContainer from "../../components/jobs-detail-pli/jobs-detail-pli.container";
|
|
import JobsDetailRates from "../../components/jobs-detail-rates/jobs-detail-rates.component";
|
|
import JobsDetailTotals from "../../components/jobs-detail-totals/jobs-detail-totals.component";
|
|
import JobsDocumentsGalleryContainer from "../../components/jobs-documents-gallery/jobs-documents-gallery.container";
|
|
import JobNotesContainer from "../../components/jobs-notes/jobs-notes.container";
|
|
import ScheduleJobModalContainer from "../../components/schedule-job-modal/schedule-job-modal.container";
|
|
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
|
import { setModalContext } from "../../redux/modals/modals.actions";
|
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
|
import JobAuditTrail from "../../components/job-audit-trail/job-audit-trail.component";
|
|
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
|
import { insertAuditTrail } from "../../redux/application/application.actions";
|
|
import JobsDocumentsLocalGallery from "../../components/jobs-documents-local-gallery/jobs-documents-local-gallery.container";
|
|
import UndefinedToNull from "../../utils/undefinedtonull";
|
|
|
|
const mapStateToProps = createStructuredSelector({
|
|
bodyshop: selectBodyshop,
|
|
jobRO: selectJobReadOnly,
|
|
});
|
|
const mapDispatchToProps = (dispatch) => ({
|
|
setPrintCenterContext: (context) =>
|
|
dispatch(setModalContext({ context: context, modal: "printCenter" })),
|
|
insertAuditTrail: ({ jobid, operation }) =>
|
|
dispatch(insertAuditTrail({ jobid, operation })),
|
|
});
|
|
export function JobsDetailPage({
|
|
bodyshop,
|
|
setPrintCenterContext,
|
|
jobRO,
|
|
job,
|
|
mutationUpdateJob,
|
|
handleSubmit,
|
|
insertAuditTrail,
|
|
refetch,
|
|
}) {
|
|
const { t } = useTranslation();
|
|
const [form] = Form.useForm();
|
|
const history = useHistory();
|
|
const [loading, setLoading] = useState(false);
|
|
const search = queryString.parse(useLocation().search);
|
|
|
|
const formItemLayout = {
|
|
layout: "vertical",
|
|
};
|
|
|
|
useEffect(() => {
|
|
//form.setFieldsValue(transormJobToForm(job));
|
|
form.resetFields();
|
|
}, [form, job]);
|
|
|
|
//useKeyboardSaveShortcut(form.submit);
|
|
|
|
const handleFinish = async (values) => {
|
|
setLoading(true);
|
|
|
|
const result = await mutationUpdateJob({
|
|
variables: {
|
|
jobId: job.id,
|
|
job: {
|
|
...UndefinedToNull(values, ["alt_transport", "category", "referral_source"]),
|
|
parts_tax_rates: {
|
|
...job.parts_tax_rates,
|
|
...values.parts_tax_rates,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
const newTotals = await Axios.post("/job/totalsssu", {
|
|
id: job.id,
|
|
});
|
|
|
|
if (newTotals.status !== 200 || result.errors) {
|
|
notification["error"]({
|
|
message: t("jobs.errors.totalscalc"),
|
|
});
|
|
} else {
|
|
notification["success"]({
|
|
message: t("jobs.successes.savetitle"),
|
|
});
|
|
const changedAuditFields = form.getFieldsValue(
|
|
[
|
|
"scheduled_in",
|
|
"actual_in",
|
|
"scheduled_completion",
|
|
"actual_completion",
|
|
"scheduled_delivery",
|
|
"actual_delivery",
|
|
"date_invoiced",
|
|
"ins_co_nm",
|
|
"ded_amt",
|
|
"ded_status",
|
|
"date_exported",
|
|
"special_coverage_policy",
|
|
"ca_gst_registrant",
|
|
"ca_bc_pvrt",
|
|
"scheduled_in",
|
|
"rate_la1",
|
|
"rate_la2",
|
|
"rate_la3",
|
|
"rate_la4",
|
|
"rate_laa",
|
|
"rate_lab",
|
|
"rate_lad",
|
|
"rate_lae",
|
|
"rate_laf",
|
|
"rate_lag",
|
|
"rate_lam",
|
|
"rate_lar",
|
|
"rate_las",
|
|
"rate_lau",
|
|
"rate_ma2s",
|
|
"rate_ma2t",
|
|
"rate_ma3s",
|
|
"rate_mabl",
|
|
"rate_macs",
|
|
"rate_mapa",
|
|
"rate_mahw",
|
|
"rate_mash",
|
|
"rate_matd",
|
|
],
|
|
(meta) => meta && meta.touched
|
|
);
|
|
|
|
Object.keys(changedAuditFields).forEach((key) => {
|
|
insertAuditTrail({
|
|
jobid: job.id,
|
|
operation: AuditTrailMapping.jobfieldchange(
|
|
key,
|
|
changedAuditFields[key] instanceof moment
|
|
? moment(changedAuditFields[key]).format("MM/DD/YYYY hh:mm a")
|
|
: changedAuditFields[key]
|
|
),
|
|
});
|
|
});
|
|
|
|
await refetch();
|
|
form.setFieldsValue(transormJobToForm(job));
|
|
form.resetFields();
|
|
}
|
|
setLoading(false);
|
|
};
|
|
|
|
const menuExtra = (
|
|
<Space wrap>
|
|
<Button
|
|
onClick={() => {
|
|
refetch();
|
|
}}
|
|
key="refresh"
|
|
>
|
|
<SyncOutlined />
|
|
{t("general.labels.refresh")}
|
|
</Button>
|
|
<JobsChangeStatus job={job} />
|
|
<JobSyncButton job={job} />
|
|
<Button
|
|
onClick={() => {
|
|
setPrintCenterContext({
|
|
actions: { refetch: refetch },
|
|
context: {
|
|
id: job.id,
|
|
job: job,
|
|
type: "job",
|
|
},
|
|
});
|
|
}}
|
|
key="printing"
|
|
>
|
|
<PrinterFilled />
|
|
{t("jobs.actions.printCenter")}
|
|
</Button>
|
|
<JobsConvertButton
|
|
job={job}
|
|
refetch={refetch}
|
|
parentFormIsFieldsTouched={form.isFieldsTouched}
|
|
/>
|
|
<JobsDetailHeaderActions key="actions" job={job} refetch={refetch} />
|
|
<Button
|
|
type="primary"
|
|
loading={loading}
|
|
disabled={jobRO}
|
|
onClick={() => form.submit()}
|
|
>
|
|
{t("general.actions.save")}
|
|
</Button>
|
|
</Space>
|
|
);
|
|
|
|
return (
|
|
<div>
|
|
<ScheduleJobModalContainer />
|
|
<JobReconciliationModal />
|
|
<JobLineUpsertModalContainer />
|
|
<Form
|
|
form={form}
|
|
name="JobDetailForm"
|
|
onFinish={handleFinish}
|
|
{...formItemLayout}
|
|
autoComplete={"off"}
|
|
initialValues={transormJobToForm(job)}
|
|
>
|
|
<PageHeader
|
|
// onBack={() => window.history.back()}
|
|
title={job.ro_number || t("general.labels.na")}
|
|
extra={menuExtra}
|
|
/>
|
|
<JobsDetailHeader job={job} />
|
|
<Divider type="horizontal" />
|
|
<FormFieldsChanged form={form} />
|
|
<Tabs
|
|
defaultActiveKey={search.tab}
|
|
onChange={(key) => history.push({ search: `?tab=${key}` })}
|
|
tabBarStyle={{ fontWeight: "bold", borderBottom: "10px" }}
|
|
>
|
|
<Tabs.TabPane
|
|
forceRender
|
|
tab={
|
|
<span>
|
|
<Icon component={FaShieldAlt} />
|
|
{t("menus.jobsdetail.general")}
|
|
</span>
|
|
}
|
|
key="general"
|
|
>
|
|
<JobsDetailGeneral job={job} form={form} />
|
|
</Tabs.TabPane>
|
|
<Tabs.TabPane
|
|
forceRender
|
|
tab={
|
|
<span>
|
|
<BarsOutlined />
|
|
{t("menus.jobsdetail.repairdata")}
|
|
</span>
|
|
}
|
|
key="repairdata"
|
|
>
|
|
<JobsLinesContainer
|
|
job={job}
|
|
joblines={job.joblines}
|
|
refetch={refetch}
|
|
form={form}
|
|
/>
|
|
</Tabs.TabPane>
|
|
<Tabs.TabPane
|
|
forceRender
|
|
tab={
|
|
<span>
|
|
<DollarCircleOutlined />
|
|
{t("menus.jobsdetail.rates")}
|
|
</span>
|
|
}
|
|
key="rates"
|
|
>
|
|
<JobsDetailRates job={job} form={form} />
|
|
</Tabs.TabPane>
|
|
<Tabs.TabPane
|
|
tab={
|
|
<span>
|
|
<DollarCircleOutlined />
|
|
{t("menus.jobsdetail.totals")}
|
|
</span>
|
|
}
|
|
key="totals"
|
|
>
|
|
<JobsDetailTotals job={job} refetch={refetch} />
|
|
</Tabs.TabPane>
|
|
<Tabs.TabPane
|
|
tab={
|
|
<span>
|
|
<ToolFilled />
|
|
{t("menus.jobsdetail.partssublet")}
|
|
</span>
|
|
}
|
|
key="partssublet"
|
|
>
|
|
<JobsDetailPliContainer job={job} />
|
|
</Tabs.TabPane>
|
|
<Tabs.TabPane
|
|
tab={
|
|
<span>
|
|
<Icon component={FaHardHat} />
|
|
{t("menus.jobsdetail.labor")}
|
|
</span>
|
|
}
|
|
key="labor"
|
|
>
|
|
<JobsDetailLaborContainer job={job} jobId={job.id} />
|
|
</Tabs.TabPane>
|
|
<Tabs.TabPane
|
|
forceRender
|
|
tab={
|
|
<span>
|
|
<CalendarFilled />
|
|
{t("menus.jobsdetail.dates")}
|
|
</span>
|
|
}
|
|
key="dates"
|
|
>
|
|
<JobsDetailDatesComponent job={job} />
|
|
</Tabs.TabPane>
|
|
<Tabs.TabPane
|
|
tab={
|
|
<span>
|
|
<FileImageFilled />
|
|
{t("jobs.labels.documents")}
|
|
</span>
|
|
}
|
|
key="documents"
|
|
>
|
|
{bodyshop.uselocalmediaserver ? (
|
|
<JobsDocumentsLocalGallery job={job} />
|
|
) : (
|
|
<JobsDocumentsGalleryContainer jobId={job.id} />
|
|
)}
|
|
</Tabs.TabPane>
|
|
<Tabs.TabPane
|
|
tab={
|
|
<span>
|
|
<Icon component={FaRegStickyNote} />
|
|
{t("jobs.labels.notes")}
|
|
</span>
|
|
}
|
|
key="notes"
|
|
>
|
|
<JobNotesContainer jobId={job.id} />
|
|
</Tabs.TabPane>
|
|
<Tabs.TabPane
|
|
tab={
|
|
<span>
|
|
<HistoryOutlined />
|
|
{t("jobs.labels.audit")}
|
|
</span>
|
|
}
|
|
key="audit"
|
|
>
|
|
<JobAuditTrail jobId={job.id} />
|
|
</Tabs.TabPane>
|
|
</Tabs>
|
|
</Form>
|
|
</div>
|
|
);
|
|
}
|
|
export default connect(mapStateToProps, mapDispatchToProps)(JobsDetailPage);
|
|
|
|
const transormJobToForm = (job) => {
|
|
return {
|
|
...job,
|
|
loss_date: job.loss_date ? moment(job.loss_date) : null,
|
|
date_estimated: job.date_estimated ? moment(job.date_estimated) : null,
|
|
};
|
|
};
|