import Icon, { BarsOutlined, CalendarFilled, DollarCircleOutlined, FileImageFilled, HistoryOutlined, PrinterFilled, SyncOutlined, ToolFilled, } from "@ant-design/icons"; import {Button, Divider, Form, notification, Space, Tabs,} from "antd"; import {PageHeader} from "@ant-design/pro-layout"; import Axios from "axios"; import dayjs from "../../utils/day"; 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 {useLocation, useNavigate} from "react-router-dom"; import {createStructuredSelector} from "reselect"; import FormFieldsChanged from "../../components/form-fields-changed-alert/form-fields-changed-alert.component"; import JobAuditTrail from "../../components/job-audit-trail/job-audit-trail.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 JobsDocumentsLocalGallery from "../../components/jobs-documents-local-gallery/jobs-documents-local-gallery.container"; import JobNotesContainer from "../../components/jobs-notes/jobs-notes.container"; import NoteUpsertModalComponent from "../../components/note-upsert-modal/note-upsert-modal.container"; import ScheduleJobModalContainer from "../../components/schedule-job-modal/schedule-job-modal.container"; import {insertAuditTrail} from "../../redux/application/application.actions"; import {selectJobReadOnly} from "../../redux/application/application.selectors"; import {setModalContext} from "../../redux/modals/modals.actions"; import {selectBodyshop} from "../../redux/user/user.selectors"; import AuditTrailMapping from "../../utils/AuditTrailMappings"; import UndefinedToNull from "../../utils/undefinedtonull"; import _ from "lodash"; import JobProfileDataWarning from "../../components/job-profile-data-warning/job-profile-data-warning.component"; import {DateTimeFormat} from "../../utils/DateFormatter"; import JobLifecycleComponent from "../../components/job-lifecycle/job-lifecycle.component"; 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 = useNavigate(); 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", ]), // The union and spread is required to keep values coming in from the estimating system that aren't displayed. parts_tax_rates: _.union( Object.keys(job.parts_tax_rates), Object.keys(values.parts_tax_rates) ).reduce((acc, val) => { acc[val] = { ...job.parts_tax_rates[val], ...values.parts_tax_rates[val], }; return acc; }, {}), materials: _.union( Object.keys(job.materials), Object.keys(values.materials) ).reduce((acc, val) => { acc[val] = { ...job.materials[val], ...values.materials[val], }; return acc; }, {}), cieca_pfl: _.union( Object.keys(job.cieca_pfl), Object.keys(values.cieca_pfl) ).reduce((acc, val) => { acc[val] = { ...job.cieca_pfl[val], ...values.cieca_pfl[val], }; return acc; }, {}), cieca_pfo: {...job.cieca_pfo, ...values.cieca_pfo}, }, }, }); try { 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 dayjs ? DateTimeFormat(changedAuditFields[key]) : changedAuditFields[key] ), }); }); await refetch(); form.setFieldsValue(transformJobToForm(job)); form.resetFields(); } } catch (error) { notification["error"]({ message: t("jobs.errors.totalscalc"), }); } finally { setLoading(false); } }; const menuExtra = ( ); return (
window.history.back()} title={job.ro_number || t("general.labels.na")} extra={menuExtra} /> history({search: `?tab=${key}`})} tabBarStyle={{fontWeight: "bold", borderBottom: "10px"}} items={[ { key: "general", icon: , label: t("menus.jobsdetail.general"), forceRender: true, children: , }, { key: "repairdata", icon: , label: t("menus.jobsdetail.repairdata"), forceRender: true, children: ( ), }, { key: "rates", icon: , label: t("menus.jobsdetail.rates"), forceRender: true, children: , }, { key: "totals", icon: , label: t("menus.jobsdetail.totals"), children: , }, { key: "partssublet", icon: , label: t("menus.jobsdetail.partssublet"), children: , }, { key: "labor", icon: , label: t("menus.jobsdetail.labor"), children: , }, { key: 'lifecycle', icon: , label: t('menus.jobsdetail.lifecycle'), children: }, { key: "dates", icon: , label: t("menus.jobsdetail.dates"), forceRender: true, children: , }, { key: "documents", icon: , label: t("jobs.labels.documents"), children: bodyshop.uselocalmediaserver ? ( ) : ( ), }, { key: "notes", icon: , label: t("jobs.labels.notes"), children: , }, { key: "audit", icon: , label: t("jobs.labels.audit"), children: , }, ]} />
); } export default connect(mapStateToProps, mapDispatchToProps)(JobsDetailPage); const transformJobToForm = (job) => { const transformedJob = {...job}; transformedJob.parts_tax_rates = Object.keys(transformedJob.parts_tax_rates).reduce((acc, parttype) => { acc[parttype] = Object.keys(transformedJob.parts_tax_rates[parttype]).reduce((innerAcc, key) => { if (key.includes("tx_in")) { innerAcc[key] = transformedJob.parts_tax_rates[parttype][key] === "Y" || transformedJob.parts_tax_rates[parttype][key] === true; } else { innerAcc[key] = transformedJob.parts_tax_rates[parttype][key]; } return innerAcc; }, {}); return acc; }, {}); transformedJob.loss_date = transformedJob.loss_date ? dayjs(transformedJob.loss_date) : null; transformedJob.date_estimated = transformedJob.date_estimated ? dayjs(transformedJob.date_estimated) : null; return transformedJob; };